/*
* Copyright 2014, 2015, 2016 Anael Mobilia
*
* This file is part of NextINpact-Unofficial.
*
* NextINpact-Unofficial is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NextINpact-Unofficial is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NextINpact-Unofficial. If not, see <http://www.gnu.org/licenses/>
*/
package com.pcinpact.network;
import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import com.pcinpact.R;
import com.pcinpact.utils.Constantes;
import com.pcinpact.utils.MyIOUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.URL;
import java.net.URLEncoder;
import javax.net.ssl.HttpsURLConnection;
/**
* Téléchargement des ressources.
*
* @author Anael
*/
public class Downloader {
/**
* Conteneur à cookies.
*/
private static CookieManager monCookieManager;
/**
* Dernier utilisateur essayé.
*/
private static String usernameLastTry = "";
/**
* Dernier mot de passe essayé.
*/
private static String passwordLastTry = "";
/**
* Jeton d'"utilisation en cours".
*/
private static Boolean isRunning = false;
/**
* Téléchargement d'une ressource
*
* @param uneURL URL de la ressource à télécharger
* @param unContext context de l'application
* @return ressource demandée brute
*/
public static byte[] download(final String uneURL, final Context unContext) {
// Retour
byte[] datas = null;
// L'utilisateur demande-t-il un debug ?
Boolean debug = Constantes.getOptionBoolean(unContext, R.string.idOptionDebug, R.bool.defautOptionDebug);
try {
// Je récupère la position du dernier slash de l'URL (précède le nom de la page)
int positionSlash = uneURL.lastIndexOf('/');
// Idem pour le ?
int positionParam = uneURL.indexOf('?');
// Le début de l'URL
String debutURL = uneURL.substring(0, positionSlash + 1);
// Le nom de la page
String page;
// Paramètres
String param = "";
// Y a-t-il un paramètre dans l'URL ?
if (positionParam != -1) {
page = uneURL.substring(positionSlash + 1, positionParam);
param = uneURL.substring(positionParam);
} else {
page = uneURL.substring(positionSlash + 1);
}
// Je créée une URL de mon String
URL monURL = new URL(debutURL + URLEncoder.encode(page, "UTF-8") + param);
try {
if (Constantes.DEBUG) {
Log.d("Downloader", "download() - Lancement connexion");
}
// J'ouvre une connection (et caste en HTTPS car images & textes HTTPS #195)
HttpsURLConnection monURLConnection = (HttpsURLConnection) monURL.openConnection();
// #214 - Définition d'un timeout pour les opérations réseaux
monURLConnection.setConnectTimeout(Constantes.TIMEOUT);
monURLConnection.setReadTimeout(Constantes.TIMEOUT);
// User Agent
monURLConnection.setRequestProperty("User-Agent", Constantes.getUserAgent(unContext));
// Vérification que tout va bien...
final int statusCode = monURLConnection.getResponseCode();
// Gestion d'un code erreur
if (statusCode != HttpsURLConnection.HTTP_OK) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "download() - Erreur " + statusCode + " au dl de " + uneURL);
}
// Retour utilisateur ?
if (debug) {
Handler handler = new Handler(unContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast monToast = Toast.makeText(unContext, "[Downloader] Erreur " + statusCode + " pour " +
uneURL, Toast.LENGTH_SHORT);
monToast.show();
}
});
}
} else {
// Je récupère le flux de données
InputStream monIS = new BufferedInputStream(monURLConnection.getInputStream());
// Le convertit en bytes (compatiblité existant...)
datas = MyIOUtils.toByteArray(monIS);
// Ferme l'IS
monIS.close();
// Ferme ma connexion
monURLConnection.disconnect();
}
} catch (IOException e) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "download() - Erreur de téléchargement pour " + uneURL, e);
}
// Retour utilisateur ?
if (debug) {
Handler handler = new Handler(unContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast monToast = Toast.makeText(unContext,
"[Downloader] Erreur de téléchargement pour l'adresse " + uneURL,
Toast.LENGTH_SHORT);
monToast.show();
}
});
}
}
} catch (Exception e) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "download() - URL erronée pour " + uneURL, e);
}
// Retour utilisateur ?
if (debug) {
Handler handler = new Handler(unContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast monToast = Toast.makeText(unContext, "[Downloader] Impossible de joindre l'adresse " + uneURL,
Toast.LENGTH_SHORT);
monToast.show();
}
});
}
}
return datas;
}
/**
* Télécharge un article "abonné".
*
* @param uneURL URL de la ressource
* @param unContext context de l'application
* @param uniquementSiConnecte dois-je télécharger uniquement si le compte abonné est connecté ?
* @return code HTML de l'article brut
*/
public static byte[] downloadArticleAbonne(final String uneURL, final Context unContext, final boolean uniquementSiConnecte) {
// Faut-il initialiser le cookie manager ?
if (monCookieManager == null) {
Downloader.initializeCookieManager();
}
// Retour
byte[] datas = null;
// Suis-je déjà connecté ?
if (estConnecte()) {
// DEBUG
if (Constantes.DEBUG) {
Log.i("Downloader", "downloadArticleAbonne() - déjà connecté => DL authentifié pour " + uneURL);
}
// Je lance le téléchargement
datas = Downloader.download(uneURL, unContext);
} else {
// J'attends si j'ai déjà une connexion en cours...
while (isRunning) {
try {
// DEBUG
if (Constantes.DEBUG) {
Log.w("Downloader", "downloadArticleAbonne() - attente de la fin d'utilisation pour " + uneURL);
}
// Attente de 0 à 0.25 seconde...
double monCoeff = Math.random();
// Evite les réveils trop simultanés (les appels l'étant...)
int maDuree = (int) (500 * monCoeff);
Thread.sleep(maDuree);
} catch (InterruptedException e) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "downloadArticleAbonne() - exception durant sleep", e);
}
}
}
// Je prends la place !
isRunning = true;
// Non connecté... suis-je connectable ?
// Chargement des identifiants
String usernameOption = Constantes.getOptionString(unContext, R.string.idOptionLogin, R.string.defautOptionLogin);
String passwordOption = Constantes.getOptionString(unContext, R.string.idOptionPassword,
R.string.defautOptionPassword);
Boolean isCompteAbonne = Constantes.getOptionBoolean(unContext, R.string.idOptionAbonne, R.bool.defautOptionAbonne);
// La connexion peut-elle être demandée ?
if (isCompteAbonne.equals(false) || "".equals(usernameOption) || "".equals(passwordOption) ||
(usernameOption.equals(usernameLastTry) && passwordOption.equals(passwordLastTry))) {
// NON : je libère le jeton d'utilisation
isRunning = false;
// Fallback est-il possible ?
if (!uniquementSiConnecte) {
// DEBUG
if (Constantes.DEBUG) {
Log.w("Downloader", "downloadArticleAbonne() - non connectable => DL non authentifié pour " + uneURL);
}
datas = Downloader.download(uneURL, unContext);
}
// Information sur l'existance du compte abonné dans les options
boolean infoAbonne = Constantes.getOptionBoolean(unContext, R.string.idOptionInfoCompteAbonne,
R.bool.defautOptionInfoCompteAbonne);
// Dois-je notifier l'utilisateur ?
if (infoAbonne) {
// Affichage d'un toast
Handler handler = new Handler(unContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast monToast = Toast.makeText(unContext, unContext.getString(R.string.infoOptionAbonne),
Toast.LENGTH_LONG);
monToast.show();
}
});
// Enregistrement de l'affichage
Constantes.setOptionBoolean(unContext, R.string.idOptionInfoCompteAbonne, false);
}
} else {
// Peut-être connectable
// DEBUG
if (Constantes.DEBUG) {
Log.w("Downloader", "downloadArticleAbonne() - lancement de l'authentification pour " + uneURL);
}
// Je lance une authentification...
connexionAbonne(unContext, usernameOption, passwordOption);
// Je libère le jeton d'utilisation
isRunning = false;
// Je relance la méthode pour avoir un résultat...
datas = downloadArticleAbonne(uneURL, unContext, uniquementSiConnecte);
}
}
return datas;
}
/**
* Initialisation du cookie manager
*/
private static void initializeCookieManager() {
// DEBUG
if (Constantes.DEBUG) {
Log.w("Downloader", "connexionAbonne() - création du CookieManager");
}
monCookieManager = new CookieManager();
monCookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
// Je définis monCookieManager comme gestionnaire des cookies
CookieHandler.setDefault(monCookieManager);
}
/**
* Connexion au compte abonné.
*
* @param unContext context de l'application
* @param username nom d'utilisateur NXI
* @param password mot de passe NXI
*/
private static void connexionAbonne(final Context unContext, final String username, final String password) {
// Enregistrement des identifiants "LastTry"
usernameLastTry = username;
passwordLastTry = password;
// Authentification sur NXI
try {
// Création de la chaîne d'authentification
String query = Constantes.AUTHENTIFICATION_USERNAME + "=" + Uri.encode(username, Constantes.NEXT_INPACT_ENCODAGE)
+ "&" + Constantes.AUTHENTIFICATION_PASSWORD + "=" + Uri.encode(password,
Constantes.NEXT_INPACT_ENCODAGE);
URL monURL = new URL(Constantes.AUTHENTIFICATION_URL);
HttpsURLConnection urlConnection = (HttpsURLConnection) monURL.openConnection();
// Gestion du timeout & useragent
urlConnection.setConnectTimeout(Constantes.TIMEOUT);
urlConnection.setReadTimeout(Constantes.TIMEOUT);
urlConnection.setRequestProperty("User-Agent", Constantes.getUserAgent(unContext));
// On envoit des données
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
// Désactivation du cache...
urlConnection.setUseCaches(false);
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
// Buffer des données et émission...
OutputStream output = new BufferedOutputStream(urlConnection.getOutputStream());
output.write(query.getBytes());
output.flush();
output.close();
urlConnection.connect();
int statusCode = urlConnection.getResponseCode();
// DEBUG
if (Constantes.DEBUG) {
//Log.d("compteAbonne", "connexionAbonne() - identifiants : " + query);
Log.d("Downloader", "connexionAbonne() - headers : " + urlConnection.getHeaderFields().toString());
// Je récupère le flux de données
InputStream monIS = new BufferedInputStream(urlConnection.getInputStream());
String datas = MyIOUtils.toString(monIS, Constantes.NEXT_INPACT_ENCODAGE);
// Ferme l'IS
monIS.close();
Log.d("Downloader", "connexionAbonne() - données : " + datas);
}
// Gestion d'un code erreur
if (statusCode != HttpsURLConnection.HTTP_OK) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "connexionAbonne() - erreur " + statusCode + " lors de l'authentification");
}
} else {
// Ai-je un cookie d'authentification ?
if (estConnecte()) {
// DEBUG
if (Constantes.DEBUG) {
Log.w("Downloader", "connexionAbonne() - authentification réussie (cookie présent)");
}
} else {
// Si non connecté
Handler handler = new Handler(unContext.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast monToast = Toast.makeText(unContext, unContext.getString(R.string.erreurAuthentification),
Toast.LENGTH_LONG);
monToast.show();
}
});
}
}
} catch (Exception e) {
// DEBUG
if (Constantes.DEBUG) {
Log.e("Downloader", "connexionAbonne() - exception durant l'authentification", e);
}
}
}
/**
* Est-on connecté (vérification du cookie).
*
* @return true si compte utilisateur connecté chez NXI
*/
public static boolean estConnecte() {
boolean monRetour = false;
// Ai-je un cookieManager ?
if (monCookieManager != null) {
// DEBUG
if (Constantes.DEBUG) {
Log.d("Downloader", "estConnecte() - cookies : " + monCookieManager.getCookieStore().getCookies().toString());
}
for (HttpCookie unCookie : monCookieManager.getCookieStore().getCookies()) {
// Est-le bon cookie ?
if (unCookie.getName().equals(Constantes.AUTHENTIFICATION_COOKIE)) {
monRetour = true;
// Pas besoin d'aller plus loin !
break;
}
}
}
return monRetour;
}
}