Java tutorial
/** * Copyright (C) 2014 by Johan von Forstner under the MIT license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software * is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package de.geeksfactory.opacclient.apis; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import de.geeksfactory.opacclient.i18n.StringProvider; import de.geeksfactory.opacclient.networking.HttpClientFactory; import de.geeksfactory.opacclient.objects.Account; import de.geeksfactory.opacclient.objects.AccountData; import de.geeksfactory.opacclient.objects.Copy; import de.geeksfactory.opacclient.objects.Detail; import de.geeksfactory.opacclient.objects.DetailedItem; import de.geeksfactory.opacclient.objects.Filter; import de.geeksfactory.opacclient.objects.Filter.Option; import de.geeksfactory.opacclient.objects.LentItem; import de.geeksfactory.opacclient.objects.Library; import de.geeksfactory.opacclient.objects.ReservedItem; import de.geeksfactory.opacclient.objects.SearchRequestResult; import de.geeksfactory.opacclient.objects.SearchResult; import de.geeksfactory.opacclient.objects.SearchResult.MediaType; import de.geeksfactory.opacclient.searchfields.DropdownSearchField; import de.geeksfactory.opacclient.searchfields.SearchField; import de.geeksfactory.opacclient.searchfields.SearchQuery; import de.geeksfactory.opacclient.searchfields.TextSearchField; import de.geeksfactory.opacclient.utils.Base64; import okhttp3.FormBody; import okhttp3.RequestBody; import okio.Buffer; import okio.BufferedSink; /** * @author Johan von Forstner, 06.04.2014 * * WebOPAC.net, Version 2.2.70 gestartet mit Gemeindebibliothek Nrensdorf (erstes * Google-Suchergebnis) * * Untersttzt bisher nur Katalogsuche, Accountunterstzung knnte (wenn keine Kontodaten * verfgbar sind) ber den Javascript-Code reverse-engineered werden: * http://www.winmedio.net/nuerensdorf/de/mobile/GetScript.ashx?id=mobile.de.min.js&v=20140122 */ /* weitere kompatible Bibliotheken: https://www.google.de/search?q=webOpac.net%202.1.30%20powered%20by%20winMedio.net&qscrl=1#q=%22webOpac.net+2.2.70+powered+by+winMedio.net%22+inurl%3Awinmedio&qscrl=1&start=0 */ public class WebOpacNet extends OkHttpBaseApi implements OpacApi { protected static HashMap<String, MediaType> defaulttypes = new HashMap<>(); static { defaulttypes.put("1", MediaType.BOOK); defaulttypes.put("2", MediaType.CD_MUSIC); defaulttypes.put("3", MediaType.AUDIOBOOK); defaulttypes.put("4", MediaType.DVD); defaulttypes.put("5", MediaType.CD_SOFTWARE); defaulttypes.put("8", MediaType.MAGAZINE); } protected String opac_url = ""; protected JSONObject data; protected List<SearchQuery> query; protected JSONObject config; protected String sessionId; @Override public void init(Library lib, HttpClientFactory httpClientFactory) { super.init(lib, httpClientFactory); this.data = lib.getData(); try { this.opac_url = data.getString("baseurl"); } catch (JSONException e) { throw new RuntimeException(e); } } @Override public void start() throws IOException { super.start(); try { config = new JSONObject(httpGet(opac_url + "/de/mobile/GetConfig.ashx", getDefaultEncoding())); } catch (JSONException e) { throw new IOException(e); } } @Override public SearchRequestResult search(List<SearchQuery> query) throws IOException, OpacErrorException, JSONException { this.query = query; if (!initialised) start(); String json = httpGet(opac_url + "/de/mobile/GetMedien.ashx?" + buildParams(query, 1), getDefaultEncoding()); return parse_search(json, 1); } protected int addParameters(SearchQuery query, StringBuilder params, int index) { if (query.getValue().equals("")) { return index; } if (index > 0) { params.append("$0"); } params.append("|").append(query.getKey()).append("|").append(query.getValue()); return index + 1; } private SearchRequestResult parse_search(String text, int page) throws OpacErrorException { if (!text.equals("")) { try { List<SearchResult> results = new ArrayList<>(); JSONObject json = new JSONObject(text); int total_result_count = Integer.parseInt(json.getString("totalcount")); JSONArray resultList = json.getJSONArray("mobmeds"); for (int i = 0; i < resultList.length(); i++) { JSONObject resultJson = resultList.getJSONObject(i); SearchResult result = new SearchResult(); result.setId(resultJson.getString("medid")); String title = resultJson.getString("titel"); String[] titleAndSubtitle = getTitleAndSubtitle(title); String publisher = resultJson.getString("verlag"); String series = resultJson.getString("reihe"); StringBuilder html = new StringBuilder(); html.append("<b>").append(titleAndSubtitle[0]).append("</b><br />"); if (titleAndSubtitle.length == 2) { html.append("<i>").append(titleAndSubtitle[1]).append("</i><br />"); } html.append(publisher).append(", ").append(series); result.setType(getMediaType(resultJson.getString("iconurl"))); result.setInnerhtml(html.toString()); if (resultJson.getString("imageurl").length() > 0) { result.setCover(resultJson.getString("imageurl")); } results.add(result); } return new SearchRequestResult(results, total_result_count, page); } catch (JSONException e) { e.printStackTrace(); throw new OpacErrorException(stringProvider .getFormattedString(StringProvider.INTERNAL_ERROR_WITH_DESCRIPTION, e.getMessage())); } } else { return new SearchRequestResult(new ArrayList<SearchResult>(), 0, page); } } private static MediaType getMediaType(String iconurl) { String number = iconurl.substring(12, 13); return defaulttypes.get(number); } @Override public SearchRequestResult filterResults(Filter filter, Option option) throws IOException, OpacErrorException { // TODO Auto-generated method stub return null; } @Override public SearchRequestResult searchGetPage(int page) throws IOException, OpacErrorException, JSONException { if (!initialised) start(); String json = httpGet(opac_url + "/de/mobile/GetMedien.ashx?" + buildParams(query, page), getDefaultEncoding()); return parse_search(json, page); } protected String buildParams(List<SearchQuery> queryList, int page) throws JSONException, OpacErrorException { int index = 0; boolean noParamsSet = true; StringBuilder queries = new StringBuilder(); if (!(queryList.size() == 1 && queryList.get(0).getKey().equals("QS"))) { queries.append("erw:0"); } for (SearchQuery query : queryList) { if (!query.getSearchField().getData().getBoolean("filter")) { noParamsSet = false; index = addParameters(query, queries, index); } } for (SearchQuery query : queryList) { if (query.getSearchField().getData().getBoolean("filter") && !query.getValue().equals("")) { noParamsSet = false; queries.append("&").append(query.getKey()).append("=").append(query.getValue()); if (query.getKey().equals("QS")) { index++; } } } StringBuilder params = new StringBuilder(); try { params.append("q=").append(URLEncoder.encode(queries.toString(), "UTF-8")); params.append("&p=").append(String.valueOf(page - 1)); // Divibib (Onleihe) if ("True".equals(config.optString("displaydivibib"))) { params.append("&nodibi=0"); } params.append("&t=1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (noParamsSet) { throw new OpacErrorException(stringProvider.getString(StringProvider.NO_CRITERIA_INPUT)); } return params.toString(); } @Override public DetailedItem getResultById(String id, String homebranch) throws IOException, OpacErrorException { String json = httpGet(opac_url + "/de/mobile/GetDetail.ashx?id=" + id + "&orientation=1", getDefaultEncoding()); return parse_detail(json); } private DetailedItem parse_detail(String text) throws OpacErrorException { try { DetailedItem result = new DetailedItem(); JSONObject json = new JSONObject(text); String title = json.getString("titel"); String[] titleAndSubtitle = getTitleAndSubtitle(title); result.setTitle(titleAndSubtitle[0]); if (titleAndSubtitle.length == 2) { result.addDetail( new Detail(stringProvider.getString(StringProvider.SUBTITLE), titleAndSubtitle[1])); } result.setCover(json.getString("imageurl")); result.setId(json.getString("medid")); // Details JSONArray info = json.getJSONArray("medium"); for (int i = 0; i < info.length(); i++) { JSONObject detailJson = info.getJSONObject(i); String name = detailJson.getString("bez"); String value = ""; JSONArray values = detailJson.getJSONArray("values"); for (int j = 0; j < values.length(); j++) { JSONObject valJson = values.getJSONObject(j); if (j != 0) { value += ", "; } String content = valJson.getString("dval"); content = content.replaceAll("<span[^>]*>", ""); content = content.replaceAll("</span>", ""); value += content; } Detail detail = new Detail(name, value); result.addDetail(detail); } if (json.has("divibib") && json.getString("divibib").length() > 1) { Detail detail = new Detail("Onleihe", json.getString("divibib").replaceAll("^.*(https?://[^\"']*)[\"'].*$", "$1")); result.addDetail(detail); } // Copies JSONArray copies = json.getJSONArray("exemplare"); for (int i = 0; i < copies.length(); i++) { JSONObject copyJson = copies.getJSONObject(i); Copy copy = new Copy(); JSONArray values = copyJson.getJSONArray("rows"); for (int j = 0; j < values.length(); j++) { JSONObject valJson = values.getJSONObject(j); String name = valJson.getString("bez"); String value = valJson.getJSONArray("values").getJSONObject(0).getString("dval"); if (!value.equals("")) { switch (name) { case "Exemplarstatus": copy.setStatus(value); break; case "Signatur": copy.setShelfmark(value); break; case "Standort": copy.setLocation(value); break; case "Themenabteilung": if (copy.getDepartment() != null) { value = copy.getDepartment() + value; } copy.setDepartment(value); break; case "Themenbereich": if (copy.getDepartment() != null) { value = copy.getDepartment() + value; } copy.setDepartment(value); break; } } } result.addCopy(copy); } result.setReservable(true); // we don't know if it's reservable until we try // there is some logic in the JavaScript code that determines whether the reservation // button is shown or not, but it is more complicated to implement as it depends on the // configuration of the OPAC return result; } catch (JSONException e) { e.printStackTrace(); throw new OpacErrorException(stringProvider .getFormattedString(StringProvider.INTERNAL_ERROR_WITH_DESCRIPTION, e.getMessage())); } } static String[] getTitleAndSubtitle(String title) { Matcher titleMatcher = Pattern.compile("<\u00ac1>([^<]*)</\u00ac1>").matcher(title); Matcher subtitleMatcher = Pattern.compile("<\u00ac2>([^<]*)</\u00ac2>").matcher(title); if (titleMatcher.find()) { String theTitle = Jsoup.parse(titleMatcher.group(1)).text(); if (subtitleMatcher.find()) { String subtitle = Jsoup.parse(subtitleMatcher.group(1)).text(); return new String[] { theTitle, subtitle }; } else { return new String[] { theTitle }; } } else { return new String[] { title }; } } @Override public DetailedItem getResult(int position) { return null; } @Override public ReservationResult reservation(DetailedItem item, Account account, int useraction, String selection) throws IOException { if (sessionId == null) { try { login(account); } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ReservationResult(MultiStepResult.Status.ERROR, e.getMessage()); } } try { if (useraction == MultiStepResult.ACTION_CONFIRMATION) { return reservationDo(item.getId(), account); } else { return reservationCheck(item.getId(), account); } } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ReservationResult(MultiStepResult.Status.ERROR, e.getMessage()); } } private ReservationResult reservationCheck(String media, Account acc) throws JSONException, IOException, OpacErrorException { JSONObject response = res(media, "3", acc); if (response.getString("possible").equals("True")) { if (response.getString("hasgebuehr").equals("1")) { ReservationResult res = new ReservationResult(MultiStepResult.Status.CONFIRMATION_NEEDED); res.setDetails(Collections.singletonList(new String[] { response.getString("gebuehr") })); return res; } else { return reservationDo(media, acc); } } else { return new ReservationResult(MultiStepResult.Status.ERROR, response.getString("message")); } } private ReservationResult reservationDo(String media, Account acc) throws IOException, JSONException, OpacErrorException { JSONObject response = res(media, "1", acc); if (response.getString("success").equals("True")) { return new ReservationResult(MultiStepResult.Status.OK); } else { return new ReservationResult(MultiStepResult.Status.ERROR, response.getString("error")); } } @Override public ProlongResult prolong(String media, Account account, int useraction, String selection) throws IOException { if (sessionId == null) { try { login(account); } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ProlongResult(MultiStepResult.Status.ERROR, e.getMessage()); } } try { if (useraction == MultiStepResult.ACTION_CONFIRMATION) { return prolongDo(media, account); } else { return prolongCheck(media, account); } } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ProlongResult(MultiStepResult.Status.ERROR, e.getMessage()); } } private ProlongResult prolongCheck(String media, Account acc) throws JSONException, IOException, OpacErrorException { JSONObject response = ausleihe(media, "3", acc); if (response.getString("possible").equals("True")) { if (!response.getString("gebuehr").equals("0.00")) { ProlongResult res = new ProlongResult(MultiStepResult.Status.CONFIRMATION_NEEDED); String fee = stringProvider.getFormattedString(StringProvider.FEE_CONFIRMATION, response.getString("gebuehr")); res.setDetails(Collections.singletonList(new String[] { fee })); return res; } else { return prolongDo(media, acc); } } else { return new ProlongResult(MultiStepResult.Status.ERROR, response.getString("message")); } } private ProlongResult prolongDo(String media, Account acc) throws IOException, JSONException, OpacErrorException { JSONObject response = ausleihe(media, "4", acc); if (response.getString("success").equals("True")) { return new ProlongResult(MultiStepResult.Status.OK); } else { return new ProlongResult(MultiStepResult.Status.ERROR, response.getString("error")); } } @Override public ProlongAllResult prolongAll(Account account, int useraction, String selection) throws IOException { if (sessionId == null) { try { login(account); } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ProlongAllResult(MultiStepResult.Status.ERROR, e.getMessage()); } } try { if (useraction == MultiStepResult.ACTION_CONFIRMATION) { return prolongAllDo(account); } else { return prolongAllCheck(account); } } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new ProlongAllResult(MultiStepResult.Status.ERROR, e.getMessage()); } } private ProlongAllResult prolongAllCheck(Account acc) throws IOException, JSONException, OpacErrorException { JSONObject response = ausleihe(null, "7", acc); if (response.getString("possible").equals("True")) { if (!response.getString("gebuehr").equals("0.00")) { ProlongAllResult res = new ProlongAllResult(MultiStepResult.Status.CONFIRMATION_NEEDED); String fee = stringProvider.getFormattedString(StringProvider.FEE_CONFIRMATION, response.getString("gebuehr")); res.setDetails(Collections.singletonList(new String[] { fee })); return res; } else { return prolongAllDo(acc); } } else { return new ProlongAllResult(MultiStepResult.Status.ERROR, response.getString("message")); } } private ProlongAllResult prolongAllDo(Account acc) throws JSONException, IOException, OpacErrorException { JSONObject response = ausleihe(null, "8", acc); if (response.getString("success").equals("True")) { return new ProlongAllResult(MultiStepResult.Status.OK); } else if (response.getString("success").equals("False") && response.getString("error").matches("Es konnten \\d+ von \\d+ Medien verlngert werden.")) { return new ProlongAllResult(MultiStepResult.Status.OK, response.getString("error")); } else { return new ProlongAllResult(MultiStepResult.Status.ERROR, response.getString("error")); } } private JSONObject ausleihe(String media, String aktion, Account acc) throws JSONException, IOException, OpacErrorException { // aktion: // 3 = check prolong, 4 = prolong // 7 = check prolong all, 8 = prolong all FormBody.Builder formData = new FormBody.Builder(Charset.forName(getDefaultEncoding())); formData.add("aktion", aktion); formData.add("zugid", media != null ? media : ""); formData.add("biblNr", "0"); formData.add("aid", ""); formData.add("sessionid", sessionId); return httpPostAccount(opac_url + "/de/mobile/Ausleihe.ashx", formData.build(), acc); } /** * Executes a HTTP POST. When the response is empty, the request is retried after login. */ private JSONObject httpPostAccount(String url, FormBody body, Account acc) throws IOException, OpacErrorException, JSONException { String s = httpPost(url, body, getDefaultEncoding()); if (s.equals("")) { login(acc); String newBody = bodyToString(body).replaceAll("sessionid=[^&]*", "sessionid=" + sessionId); s = httpPost(url, new RequestBody() { @Override public okhttp3.MediaType contentType() { return okhttp3.MediaType.parse("application/x-www-form-urlencoded"); } @Override public void writeTo(BufferedSink sink) throws IOException { sink.write(newBody.getBytes(getDefaultEncoding())); } }, getDefaultEncoding()); } return new JSONObject(s); } private String bodyToString(FormBody body) throws IOException { Buffer buffer = new Buffer(); body.writeTo(buffer); return buffer.readUtf8(); } @Override public CancelResult cancel(String media, Account account, int useraction, String selection) throws IOException { if (sessionId == null) { try { login(account); } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new CancelResult(MultiStepResult.Status.ERROR, e.getMessage()); } } try { JSONObject response = res(media, "2", account); if (response.getString("success").equals("True")) { return new CancelResult(MultiStepResult.Status.OK); } else { return new CancelResult(MultiStepResult.Status.ERROR, response.getString("error")); } } catch (JSONException e) { throw new IOException(e); } catch (OpacErrorException e) { return new CancelResult(MultiStepResult.Status.ERROR, e.getMessage()); } } private JSONObject res(String media, String aktion, Account acc) throws JSONException, IOException, OpacErrorException { // aktion: // 1 = make reservation // 2 = cancel reservation // 3 = check reservation FormBody.Builder formData = new FormBody.Builder(Charset.forName(getDefaultEncoding())); formData.add("aktion", aktion); if (aktion.equals("2")) { String medid = media.split("Z")[0].replace("dat", ""); String resid = media.split("Z")[1]; formData.add("medid", medid); formData.add("resid", resid); } else { formData.add("medid", media); formData.add("bemerkung", ""); formData.add("zws", ""); } formData.add("biblNr", "0"); formData.add("sessionid", sessionId); return httpPostAccount(opac_url + "/de/mobile/Res.ashx", formData.build(), acc); } @Override public AccountData account(Account account) throws IOException, JSONException, OpacErrorException { if (sessionId == null) login(account); FormBody.Builder formData = new FormBody.Builder(Charset.forName(getDefaultEncoding())); formData.add("art", "7"); formData.add("rsa", ""); formData.add("sessionId", sessionId); JSONObject response = httpPostAccount(opac_url + "/de/mobile/Konto.ashx", formData.build(), account); AccountData data = new AccountData(account.getId()); parseAccount(response, data); return data; } static void parseAccount(JSONObject response, AccountData data) throws JSONException { data.setValidUntil(ifNotEmpty(response.getString("gueltigbis"))); data.setPendingFees(ifNotEmpty(response.getString("gebuehren"))); DateTimeFormatter format = DateTimeFormat.forPattern("dd.MM.yyyy"); List<LentItem> lent = new ArrayList<>(); JSONArray ausleihen = response.getJSONArray("ausleihen"); for (int i = 0; i < ausleihen.length(); i++) { LentItem item = new LentItem(); JSONObject json = ausleihen.getJSONObject(i); item.setAuthor(json.getString("urheber")); item.setTitle(json.getString("titelkurz").replace(item.getAuthor() + " : ", "")); item.setCover(json.getString("imageurl")); item.setMediaType(getMediaType(json.getString("iconurl"))); item.setStatus(ifNotEmpty(json.getString("hinweis"))); item.setProlongData(json.getString("exemplarid")); JSONArray felder = json.getJSONArray("felder"); for (int j = 0; j < felder.length(); j++) { String value = felder.getJSONObject(j).getString("display"); if (value.startsWith("Leihfrist: ")) { String dateStr = value.replace("Leihfrist: ", ""); item.setDeadline(format.parseLocalDate(dateStr)); break; } } lent.add(item); } data.setLent(lent); List<ReservedItem> reservations = new ArrayList<>(); JSONArray reservationen = response.getJSONArray("reservationen"); for (int i = 0; i < reservationen.length(); i++) { ReservedItem item = new ReservedItem(); JSONObject json = reservationen.getJSONObject(i); item.setAuthor(json.getString("urheber")); item.setTitle(json.getString("titelkurz").replace(item.getAuthor() + " : ", "")); item.setCover(json.getString("imageurl")); item.setMediaType(getMediaType(json.getString("iconurl"))); item.setStatus(ifNotEmpty(json.getString("hinweis"))); if (!json.getString("abholdat").equals("")) { item.setExpirationDate(format.parseLocalDate(json.getString("abholdat"))); } if (json.getString("status").equals("1")) { item.setCancelData(json.getString("exemplarid")); } reservations.add(item); } data.setLent(lent); data.setReservations(reservations); } private static String ifNotEmpty(String value) { if (value == null || value.equals("")) { return null; } else { return value; } } private void login(Account account) throws IOException, JSONException, OpacErrorException { String toEncrypt = account.getName() + "|" + account.getPassword() + "|"; // + stammbibliothek + "|" toEncrypt += randomString(); JSONObject response = new JSONObject( httpGet(opac_url + "/de/mobile/GetRsaPublic.ashx", getDefaultEncoding())); BigInteger key = new BigInteger(response.getString("key"), 16); BigInteger modulus = new BigInteger(response.getString("modulus"), 16); try { final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(key, modulus))); byte[] result = cipher.doFinal(toEncrypt.getBytes()); String str = Base64.encodeBytes(result); FormBody.Builder formData = new FormBody.Builder(Charset.forName(getDefaultEncoding())); formData.add("art", "1"); formData.add("rsa", str); response = new JSONObject( httpPost(opac_url + "/de/mobile/Konto.ashx", formData.build(), getDefaultEncoding())); if (!response.optString("success", "0").equals("1")) { throw new OpacErrorException(stringProvider.getString(StringProvider.WRONG_LOGIN_DATA)); } this.sessionId = response.getString("sessionid"); } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | InvalidKeyException | IllegalBlockSizeException | InvalidKeySpecException e) { e.printStackTrace(); } } private static String randomString() { StringBuilder a = new StringBuilder(); for (int b = 0; b < 12; b++) { int c = (int) Math.floor(Math.random() * 61); a.append("0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".substring(c, c + 1)); } return a.toString(); } @Override public List<SearchField> parseSearchFields() throws IOException, JSONException { if (!initialised) start(); List<SearchField> fields = new ArrayList<>(); // Text fields String html = httpGet(opac_url + "/de/mobile/default.aspx", getDefaultEncoding()); Document doc = Jsoup.parse(html); Elements options = doc.select("#drpOptSearchT option"); for (Element option : options) { TextSearchField field = new TextSearchField(); field.setDisplayName(option.text()); field.setId(option.attr("value")); field.setData(new JSONObject("{\"filter\":false}")); field.setHint(""); fields.add(field); } // Dropdowns String text = httpGet(opac_url + "/de/mobile/GetRestrictions.ashx", getDefaultEncoding()); JSONArray filters = new JSONObject(text).getJSONArray("restrcontainers"); for (int i = 0; i < filters.length(); i++) { JSONObject filter = filters.getJSONObject(i); if (filter.getString("querytyp").equals("EJ")) { // Querying by year also works for other years than the ones // listed // -> Make it a text field instead of a dropdown TextSearchField field = new TextSearchField(); field.setDisplayName(filter.getString("kopf")); field.setId(filter.getString("querytyp")); field.setData(new JSONObject("{\"filter\":true}")); field.setHint(""); fields.add(field); } else { DropdownSearchField field = new DropdownSearchField(); field.setId(filter.getString("querytyp")); field.setDisplayName(filter.getString("kopf")); JSONArray restrictions = filter.getJSONArray("restrictions"); field.addDropdownValue("", "Alle"); for (int j = 0; j < restrictions.length(); j++) { JSONObject restriction = restrictions.getJSONObject(j); field.addDropdownValue(restriction.getString("id"), restriction.getString("bez")); } field.setData(new JSONObject("{\"filter\":true}")); fields.add(field); } } return fields; } @Override public String getShareUrl(String id, String title) { return opac_url + "/default.aspx?id=" + id; } @Override public int getSupportFlags() { return SUPPORT_FLAG_ENDLESS_SCROLLING | SUPPORT_FLAG_CHANGE_ACCOUNT | SUPPORT_FLAG_WARN_PROLONG_FEES | SUPPORT_FLAG_WARN_RESERVATION_FEES | SUPPORT_FLAG_ACCOUNT_PROLONG_ALL; } @Override protected String getDefaultEncoding() { return "UTF-8"; } @Override public void checkAccountData(Account account) throws IOException, JSONException, OpacErrorException { login(account); } @Override public void setLanguage(String language) { // TODO Auto-generated method stub } @Override public Set<String> getSupportedLanguages() throws IOException { // TODO Auto-generated method stub return null; } }