/*
* Copyright (C) 2010-2011 Geometer Plus <contact@geometerplus.com>
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
package org.geometerplus.fbreader.network.authentication.litres;
import java.util.*;
import org.geometerplus.zlibrary.core.options.ZLBooleanOption;
import org.geometerplus.zlibrary.core.options.ZLStringOption;
import org.geometerplus.zlibrary.core.util.ZLNetworkUtil;
import org.geometerplus.zlibrary.core.network.ZLNetworkManager;
import org.geometerplus.zlibrary.core.network.ZLNetworkException;
import org.geometerplus.zlibrary.core.network.ZLNetworkRequest;
import org.geometerplus.fbreader.network.*;
import org.geometerplus.fbreader.network.opds.OPDSNetworkLink;
import org.geometerplus.fbreader.network.authentication.*;
import org.geometerplus.fbreader.network.urlInfo.*;
public class LitResAuthenticationManager extends NetworkAuthenticationManager {
private volatile boolean myFullyInitialized;
private final ZLStringOption mySidOption;
private final ZLStringOption myUserIdOption;
private final ZLBooleanOption myCanRebillOption;
private volatile String myInitializedDataSid;
private volatile String myAccount;
private final Map<String,NetworkBookItem> myPurchasedBookMap =
new HashMap<String,NetworkBookItem>();
private final List<NetworkBookItem> myPurchasedBookList =
new LinkedList<NetworkBookItem>();
public LitResAuthenticationManager(OPDSNetworkLink link) {
super(link, null);
mySidOption = new ZLStringOption(link.getSiteName(), "sid", "");
myUserIdOption = new ZLStringOption(link.getSiteName(), "userId", "");
myCanRebillOption = new ZLBooleanOption(link.getSiteName(), "canRebill", false);
}
public synchronized void initUser(String userName, String sid, String userId, boolean canRebill) {
UserNameOption.setValue(userName);
mySidOption.setValue(sid);
myUserIdOption.setValue(userId);
myCanRebillOption.setValue(canRebill);
myFullyInitialized = !"".equals(userName) && !"".equals(sid) && !"".equals(userId);
}
@Override
public void logOut() {
logOut(true);
}
private synchronized void logOut(boolean full) {
if (full) {
initUser(UserNameOption.getValue(), "", "", false);
} else {
myFullyInitialized = false;
}
myInitializedDataSid = null;
myPurchasedBookMap.clear();
myPurchasedBookList.clear();
}
@Override
public boolean isAuthorised(boolean useNetwork) throws ZLNetworkException {
final String sid;
synchronized (this) {
boolean authState =
UserNameOption.getValue().length() != 0 &&
mySidOption.getValue().length() != 0;
if (myFullyInitialized || !useNetwork) {
return authState;
}
if (!authState) {
logOut(false);
return false;
}
sid = mySidOption.getValue();
}
String url = Link.getUrl(UrlInfo.Type.SignIn);
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}
final LitResLoginXMLReader xmlReader = new LitResLoginXMLReader(Link.getSiteName());
ZLNetworkException exception = null;
try {
final LitResNetworkRequest request = new LitResNetworkRequest(url, xmlReader);
request.addPostParameter("sid", sid);
ZLNetworkManager.Instance().perform(request);
} catch (ZLNetworkException e) {
exception = e;
}
synchronized (this) {
if (exception != null) {
if (NetworkException.ERROR_AUTHENTICATION_FAILED.equals(exception.getCode())) {
throw exception;
}
logOut(false);
return false;
}
initUser(UserNameOption.getValue(), xmlReader.Sid, xmlReader.UserId, xmlReader.CanRebill);
return true;
}
}
@Override
public void authorise(String password) throws ZLNetworkException {
String url = Link.getUrl(UrlInfo.Type.SignIn);
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}
final String login;
synchronized (this) {
login = UserNameOption.getValue();
}
final LitResLoginXMLReader xmlReader = new LitResLoginXMLReader(Link.getSiteName());
ZLNetworkException exception = null;
try {
final LitResNetworkRequest request = new LitResNetworkRequest(url, xmlReader);
request.addPostParameter("login", login);
request.addPostParameter("pwd", password);
ZLNetworkManager.Instance().perform(request);
} catch (ZLNetworkException e) {
exception = e;
}
synchronized (this) {
if (exception != null) {
logOut(false);
throw exception;
}
initUser(UserNameOption.getValue(), xmlReader.Sid, xmlReader.UserId, xmlReader.CanRebill);
}
}
@Override
public BookUrlInfo downloadReference(NetworkBookItem book) {
final String sid;
synchronized (this) {
sid = mySidOption.getValue();
}
if (sid.length() == 0) {
return null;
}
BookUrlInfo reference = book.reference(UrlInfo.Type.BookConditional);
if (reference == null) {
return null;
}
final String url = ZLNetworkUtil.appendParameter(reference.Url, "sid", sid);
return new DecoratedBookUrlInfo(reference, url);
}
@Override
public String currentUserName() {
final String value;
synchronized (this) {
value = UserNameOption.getValue();
}
if (value.length() == 0) {
return null;
}
return value;
}
@Override
public synchronized boolean needPurchase(NetworkBookItem book) {
return !myPurchasedBookMap.containsKey(book.Id);
}
@Override
public void purchaseBook(NetworkBookItem book) throws ZLNetworkException {
final String sid;
synchronized (this) {
sid = mySidOption.getValue();
}
if (sid.length() == 0) {
throw new ZLNetworkException(NetworkException.ERROR_AUTHENTICATION_FAILED);
}
final BookUrlInfo reference = book.reference(UrlInfo.Type.BookBuy);
if (reference == null) {
throw new ZLNetworkException(NetworkException.ERROR_BOOK_NOT_PURCHASED); // TODO: more correct error message???
}
final LitResPurchaseXMLReader xmlReader = new LitResPurchaseXMLReader(Link.getSiteName());
ZLNetworkException exception = null;
try {
final LitResNetworkRequest request = new LitResNetworkRequest(reference.Url, xmlReader);
request.addPostParameter("sid", sid);
ZLNetworkManager.Instance().perform(request);
} catch (ZLNetworkException e) {
exception = e;
}
synchronized (this) {
if (xmlReader.Account != null) {
myAccount = BookBuyUrlInfo.price(xmlReader.Account, "RUB");
}
if (exception != null) {
final String code = exception.getCode();
if (NetworkException.ERROR_AUTHENTICATION_FAILED.equals(code)) {
logOut(false);
} else if (NetworkException.ERROR_PURCHASE_ALREADY_PURCHASED.equals(code)) {
myPurchasedBookMap.put(book.Id, book);
myPurchasedBookList.add(0, book);
}
throw exception;
}
if (xmlReader.BookId == null || !xmlReader.BookId.equals(book.Id)) {
throw new ZLNetworkException(NetworkException.ERROR_SOMETHING_WRONG, Link.getSiteName());
}
myPurchasedBookMap.put(book.Id, book);
myPurchasedBookList.add(0, book);
}
}
@Override
public String topupLink() {
final String sid;
synchronized (this) {
sid = mySidOption.getValue();
}
if (sid.length() == 0) {
return null;
}
final String url = Link.getUrl(UrlInfo.Type.TopUp);
if (url == null) {
return null;
}
return ZLNetworkUtil.appendParameter(url, "sid", sid);
}
@Override
public synchronized String currentAccount() {
return myAccount;
}
void reloadPurchasedBooks() throws ZLNetworkException {
final LitResNetworkRequest networkRequest;
synchronized (this) {
final String sid = mySidOption.getValue();
if (sid.length() == 0) {
throw new ZLNetworkException(NetworkException.ERROR_AUTHENTICATION_FAILED);
}
if (!sid.equals(myInitializedDataSid)) {
logOut(false);
throw new ZLNetworkException(NetworkException.ERROR_AUTHENTICATION_FAILED);
}
networkRequest = loadPurchasedBooks();
}
ZLNetworkException exception = null;
try {
ZLNetworkManager.Instance().perform(networkRequest);
} catch (ZLNetworkException e) {
exception = e;
}
synchronized (this) {
if (exception != null) {
//loadPurchasedBooksOnError();
if (NetworkException.ERROR_AUTHENTICATION_FAILED.equals(exception.getCode())) {
logOut(false);
}
throw exception;
}
loadPurchasedBooksOnSuccess(networkRequest);
}
}
@Override
public synchronized List<NetworkBookItem> purchasedBooks() {
return Collections.unmodifiableList(myPurchasedBookList);
}
@Override
public synchronized boolean needsInitialization() {
final String sid = mySidOption.getValue();
if (sid.length() == 0) {
return false;
}
return !sid.equals(myInitializedDataSid);
}
@Override
public void initialize() throws ZLNetworkException {
final String sid;
final LitResNetworkRequest purchasedBooksRequest;
final LitResNetworkRequest accountRequest;
synchronized (this) {
sid = mySidOption.getValue();
if (sid.length() == 0) {
throw new ZLNetworkException(NetworkException.ERROR_AUTHENTICATION_FAILED);
}
if (sid.equals(myInitializedDataSid) || !isAuthorised(true)) {
return;
}
purchasedBooksRequest = loadPurchasedBooks();
accountRequest = loadAccount();
}
final LinkedList<ZLNetworkRequest> requests = new LinkedList<ZLNetworkRequest>();
requests.add(purchasedBooksRequest);
requests.add(accountRequest);
ZLNetworkException exception = null;
try {
ZLNetworkManager.Instance().perform(requests);
} catch (ZLNetworkException e) {
exception = e;
}
synchronized (this) {
if (exception != null) {
myInitializedDataSid = null;
loadPurchasedBooksOnError();
loadAccountOnError();
throw exception;
}
myInitializedDataSid = sid;
loadPurchasedBooksOnSuccess(purchasedBooksRequest);
loadAccountOnSuccess(accountRequest);
}
}
private LitResNetworkRequest loadPurchasedBooks() {
final String sid = mySidOption.getValue();
final String query = "pages/catalit_browser/";
final LitResNetworkRequest request = new LitResNetworkRequest(
LitResUtil.url(Link, query),
new LitResXMLReader((OPDSNetworkLink)Link, new LinkedList<NetworkItem>())
);
request.addPostParameter("my", "1");
request.addPostParameter("sid", sid);
return request;
}
private void loadPurchasedBooksOnError() {
myPurchasedBookMap.clear();
myPurchasedBookList.clear();
}
private void loadPurchasedBooksOnSuccess(LitResNetworkRequest purchasedBooksRequest) {
LitResXMLReader reader = (LitResXMLReader)purchasedBooksRequest.Reader;
myPurchasedBookMap.clear();
myPurchasedBookList.clear();
for (NetworkItem item : reader.Books) {
if (item instanceof NetworkBookItem) {
NetworkBookItem book = (NetworkBookItem)item;
myPurchasedBookMap.put(book.Id, book);
myPurchasedBookList.add(book);
}
}
}
private LitResNetworkRequest loadAccount() {
final String sid = mySidOption.getValue();
final String query = "pages/purchase_book/";
final LitResNetworkRequest request = new LitResNetworkRequest(
LitResUtil.url(Link, query),
new LitResPurchaseXMLReader(Link.getSiteName())
);
request.addPostParameter("sid", sid);
request.addPostParameter("art", "0");
return request;
}
private void loadAccountOnError() {
myAccount = null;
}
private void loadAccountOnSuccess(LitResNetworkRequest accountRequest) {
LitResPurchaseXMLReader reader = (LitResPurchaseXMLReader)accountRequest.Reader;
myAccount = BookBuyUrlInfo.price(reader.Account, "RUB");
}
@Override
public boolean passwordRecoverySupported() {
return true;
}
@Override
public void recoverPassword(String email) throws ZLNetworkException {
final String url = Link.getUrl(UrlInfo.Type.RecoverPassword);
if (url == null) {
throw new ZLNetworkException(NetworkException.ERROR_UNSUPPORTED_OPERATION);
}
final LitResPasswordRecoveryXMLReader xmlReader = new LitResPasswordRecoveryXMLReader(Link.getSiteName());
final LitResNetworkRequest request = new LitResNetworkRequest(url, xmlReader);
request.addPostParameter("mail", email);
ZLNetworkManager.Instance().perform(request);
}
@Override
public Map<String,String> getTopupData() {
final HashMap<String,String> map = new HashMap<String,String>();
map.put("litres:userId", myUserIdOption.getValue());
map.put("litres:canRebill", myCanRebillOption.getValue() ? "true" : "false");
map.put("litres:sid", mySidOption.getValue());
return map;
}
}