forked from soutade/libgourou
		
	Add adept_loan_mgt util
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
TARGETS=acsmdownloader adept_activate adept_remove
 | 
					TARGETS=acsmdownloader adept_activate adept_remove adept_loan_mgt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
 | 
					CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -18,17 +18,26 @@ else
 | 
				
			|||||||
CXXFLAGS += -O2
 | 
					CXXFLAGS += -O2
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COMMON_DEPS = drmprocessorclientimpl.cpp utils_common.cpp $(STATIC_DEP)
 | 
					COMMON_DEPS = drmprocessorclientimpl.cpp utils_common.cpp
 | 
				
			||||||
 | 
					COMMON_OBJECTS = $(COMMON_DEPS:.cpp=.o)
 | 
				
			||||||
 | 
					COMMON_LIB  = utils.a
 | 
				
			||||||
 | 
					
 | 
				
			||||||
all: $(TARGETS)
 | 
					all: $(TARGETS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
acsmdownloader: acsmdownloader.cpp $(COMMON_DEPS)
 | 
					${COMMON_LIB}: ${COMMON_DEPS} ${STATIC_DEP}
 | 
				
			||||||
 | 
						$(CXX) $(CXXFLAGS) ${COMMON_DEPS} $(LDFLAGS) -c
 | 
				
			||||||
 | 
						$(AR) crs $@ ${COMMON_OBJECTS} $(STATIC_DEP)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					acsmdownloader: acsmdownloader.cpp ${COMMON_LIB}
 | 
				
			||||||
	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
						$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
adept_activate: adept_activate.cpp $(COMMON_DEPS)
 | 
					adept_activate: adept_activate.cpp ${COMMON_LIB}
 | 
				
			||||||
	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
						$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
adept_remove: adept_remove.cpp $(COMMON_DEPS)
 | 
					adept_remove: adept_remove.cpp ${COMMON_LIB}
 | 
				
			||||||
 | 
						$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					adept_loan_mgt: adept_loan_mgt.cpp ${COMMON_LIB}
 | 
				
			||||||
	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
						$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,12 +26,15 @@
 | 
				
			|||||||
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
					  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 #include <getopt.h>
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					#include <libgen.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <libgourou.h>
 | 
					#include <libgourou.h>
 | 
				
			||||||
 | 
					#include <libgourou_common.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "drmprocessorclientimpl.h"
 | 
					#include "drmprocessorclientimpl.h"
 | 
				
			||||||
#include "utils_common.h"
 | 
					#include "utils_common.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -54,7 +57,6 @@ public:
 | 
				
			|||||||
	int ret = 0;
 | 
						int ret = 0;
 | 
				
			||||||
	try
 | 
						try
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	    DRMProcessorClientImpl client;
 | 
					 | 
				
			||||||
	    gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
 | 
						    gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
 | 
				
			||||||
	    gourou::User* user = processor.getUser();
 | 
						    gourou::User* user = processor.getUser();
 | 
				
			||||||
	    
 | 
						    
 | 
				
			||||||
@@ -118,6 +120,8 @@ public:
 | 
				
			|||||||
		    filename = finalName;
 | 
							    filename = finalName;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		std::cout << "Created " << filename << std::endl;
 | 
							std::cout << "Created " << filename << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							serializeLoanToken(item);
 | 
				
			||||||
	    }
 | 
						    }
 | 
				
			||||||
	} catch(std::exception& e)
 | 
						} catch(std::exception& e)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -127,6 +131,52 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void serializeLoanToken(gourou::FulfillmentItem* item)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						gourou::LoanToken* token = item->getLoanToken();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// No loan token available
 | 
				
			||||||
 | 
						if (!token)
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pugi::xml_document doc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pugi::xml_node decl = doc.append_child(pugi::node_declaration);
 | 
				
			||||||
 | 
						decl.append_attribute("version") = "1.0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pugi::xml_node root = doc.append_child("loanToken");
 | 
				
			||||||
 | 
						gourou::appendTextElem(root, "id",          (*token)["id"]);
 | 
				
			||||||
 | 
						gourou::appendTextElem(root, "operatorURL", (*token)["operatorURL"]);
 | 
				
			||||||
 | 
						gourou::appendTextElem(root, "validity",    (*token)["validity"]);
 | 
				
			||||||
 | 
						gourou::appendTextElem(root, "name",        item->getMetadata("title"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char * activationDir = strdup(deviceFile);
 | 
				
			||||||
 | 
						activationDir = dirname(activationDir);
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
						gourou::StringXMLWriter xmlWriter;
 | 
				
			||||||
 | 
						doc.save(xmlWriter, "  ");
 | 
				
			||||||
 | 
						std::string xmlStr = xmlWriter.getResult();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Use first bytes of SHA1(id) as filename
 | 
				
			||||||
 | 
						unsigned char sha1[gourou::SHA1_LEN];
 | 
				
			||||||
 | 
						client.digest("SHA1", (unsigned char*)(*token)["id"].c_str(), (*token)["id"].size(), sha1);
 | 
				
			||||||
 | 
						gourou::ByteArray tmp(sha1, sizeof(sha1));
 | 
				
			||||||
 | 
						std::string filenameHex = tmp.toHex();
 | 
				
			||||||
 | 
						std::string filename(filenameHex.c_str(), ID_HASH_SIZE);
 | 
				
			||||||
 | 
						std::string fullPath = std::string(activationDir);
 | 
				
			||||||
 | 
						fullPath += std::string ("/") + std::string(LOANS_DIR);
 | 
				
			||||||
 | 
						mkpath(fullPath.c_str());
 | 
				
			||||||
 | 
						fullPath += filename + std::string(".xml");
 | 
				
			||||||
 | 
						gourou::writeFile(fullPath, xmlStr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::cout << "Loan token serialized into " << fullPath << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free(activationDir);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    DRMProcessorClientImpl client;
 | 
				
			||||||
};	      
 | 
					};	      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										479
									
								
								utils/adept_loan_mgt.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										479
									
								
								utils/adept_loan_mgt.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,479 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					  Copyright (c) 2022, Grégory Soutadé
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  All rights reserved.
 | 
				
			||||||
 | 
					  Redistribution and use in source and binary forms, with or without
 | 
				
			||||||
 | 
					  modification, are permitted provided that the following conditions are met:
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  * Redistributions of source code must retain the above copyright
 | 
				
			||||||
 | 
					    notice, this list of conditions and the following disclaimer.
 | 
				
			||||||
 | 
					  * Redistributions in binary form must reproduce the above copyright
 | 
				
			||||||
 | 
					    notice, this list of conditions and the following disclaimer in the
 | 
				
			||||||
 | 
					    documentation and/or other materials provided with the distribution.
 | 
				
			||||||
 | 
					  * Neither the name of the copyright holder nor the
 | 
				
			||||||
 | 
					    names of its contributors may be used to endorse or promote products
 | 
				
			||||||
 | 
					    derived from this software without specific prior written permission.
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 | 
				
			||||||
 | 
					  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
				
			||||||
 | 
					  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
				
			||||||
 | 
					  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 | 
				
			||||||
 | 
					  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
				
			||||||
 | 
					  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
				
			||||||
 | 
					  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
				
			||||||
 | 
					  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
				
			||||||
 | 
					  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
				
			||||||
 | 
					  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <getopt.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define _XOPEN_SOURCE 700
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <dirent.h>
 | 
				
			||||||
 | 
					#include <libgen.h>
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <libgourou.h>
 | 
				
			||||||
 | 
					#include <libgourou_common.h>
 | 
				
			||||||
 | 
					#include "drmprocessorclientimpl.h"
 | 
				
			||||||
 | 
					#include "utils_common.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_SIZE_BOOK_NAME   30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static       char* activationDir  = 0;
 | 
				
			||||||
 | 
					static const char* deviceFile     = "device.xml";
 | 
				
			||||||
 | 
					static const char* activationFile = "activation.xml";
 | 
				
			||||||
 | 
					static const char* devicekeyFile  = "devicesalt";
 | 
				
			||||||
 | 
					static       bool  list           = false;
 | 
				
			||||||
 | 
					static const char* returnID       = 0;
 | 
				
			||||||
 | 
					static const char* deleteID       = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Loan
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::string id;
 | 
				
			||||||
 | 
					    std::string operatorURL;
 | 
				
			||||||
 | 
					    std::string validity;
 | 
				
			||||||
 | 
					    std::string bookName;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    std::string path;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					class LoanMGT
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    ~LoanMGT()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						for (const auto& kv : loanedBooks)
 | 
				
			||||||
 | 
						    delete kv.second;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    int run()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						int ret = 0;
 | 
				
			||||||
 | 
						try
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    DRMProcessorClientImpl client;
 | 
				
			||||||
 | 
						    gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    loadLoanedBooks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    if (list)
 | 
				
			||||||
 | 
							displayLoanList();
 | 
				
			||||||
 | 
						    else if (returnID)
 | 
				
			||||||
 | 
							returnBook(processor);
 | 
				
			||||||
 | 
						    else if (deleteID)
 | 
				
			||||||
 | 
							deleteLoan();
 | 
				
			||||||
 | 
						} catch(std::exception& e)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << e.what() << std::endl;
 | 
				
			||||||
 | 
						    ret = 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    void loadLoanedBooks()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						DIR *dp;
 | 
				
			||||||
 | 
						struct dirent *ep;
 | 
				
			||||||
 | 
						int entryLen;
 | 
				
			||||||
 | 
						struct Loan* loan;
 | 
				
			||||||
 | 
						char * res;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::string loanDir = std::string(activationDir) + std::string("/") + LOANS_DIR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!fileExists(loanDir.c_str()))
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						dp = opendir (loanDir.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if(!dp)
 | 
				
			||||||
 | 
						    EXCEPTION(gourou::USER_INVALID_INPUT, "Cannot read directory " << loanDir);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while ((ep = readdir (dp)))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    if (ep->d_type != DT_LNK &&
 | 
				
			||||||
 | 
							ep->d_type != DT_REG)
 | 
				
			||||||
 | 
							continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    entryLen = strlen(ep->d_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    if (entryLen <= 4 ||
 | 
				
			||||||
 | 
							ep->d_name[entryLen-4] != '.' ||
 | 
				
			||||||
 | 
							ep->d_name[entryLen-3] != 'x' ||
 | 
				
			||||||
 | 
							ep->d_name[entryLen-2] != 'm' ||
 | 
				
			||||||
 | 
							ep->d_name[entryLen-1] != 'l')
 | 
				
			||||||
 | 
							continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    std::string id = std::string(ep->d_name, entryLen-4);
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						    loan = new Loan;
 | 
				
			||||||
 | 
						    loan->path = loanDir + std::string("/") + ep->d_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    pugi::xml_document xmlDoc;
 | 
				
			||||||
 | 
						    pugi::xml_node node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    if (!xmlDoc.load_file(loan->path.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Invalid loan entry " << loan->path << std::endl;
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // id
 | 
				
			||||||
 | 
						    node = xmlDoc.select_node("//id").node();
 | 
				
			||||||
 | 
						    if (!node)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Invalid loan entry " << ep->d_name << ", no id element" << std::endl;
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    loan->id = node.first_child().value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // operatorURL
 | 
				
			||||||
 | 
						    node = xmlDoc.select_node("//operatorURL").node();
 | 
				
			||||||
 | 
						    if (!node)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Invalid loan entry " << ep->d_name << ", no operatorURL element" << std::endl;
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    loan->operatorURL = node.first_child().value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // validity
 | 
				
			||||||
 | 
						    node = xmlDoc.select_node("//validity").node();
 | 
				
			||||||
 | 
						    if (!node)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Invalid loan entry " << ep->d_name << ", no validity element" << std::endl;
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    loan->validity = node.first_child().value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    // bookName
 | 
				
			||||||
 | 
						    node = xmlDoc.select_node("//name").node();
 | 
				
			||||||
 | 
						    if (!node)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Invalid loan entry " << ep->d_name << ", no name element" << std::endl;
 | 
				
			||||||
 | 
							goto error;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    loan->bookName = node.first_child().value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    struct tm tm;
 | 
				
			||||||
 | 
						    res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm);
 | 
				
			||||||
 | 
						    if (*res == 0)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							if (mktime(&tm) <= time(NULL))
 | 
				
			||||||
 | 
							    loan->validity = "     (Expired)";
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    else
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							std::cout << "Unable to parse validity timestamp :" << loan->validity << std::endl;
 | 
				
			||||||
 | 
							loan->validity = "     (Unknown)";
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						    loanedBooks[id] = loan;
 | 
				
			||||||
 | 
						    continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error:
 | 
				
			||||||
 | 
						    if (loan)
 | 
				
			||||||
 | 
							delete loan;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						closedir (dp);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void displayLoanList()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						if (!loanedBooks.size())
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Any book loaned" << std::endl;
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct Loan* loan;
 | 
				
			||||||
 | 
						unsigned int maxSizeBookName=0;
 | 
				
			||||||
 | 
						// Compute max size
 | 
				
			||||||
 | 
						for (const auto& kv : loanedBooks)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    loan = kv.second;
 | 
				
			||||||
 | 
						    if (loan->bookName.size() > maxSizeBookName)
 | 
				
			||||||
 | 
							maxSizeBookName = loan->bookName.size();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (maxSizeBookName > MAX_SIZE_BOOK_NAME)
 | 
				
			||||||
 | 
						    maxSizeBookName = MAX_SIZE_BOOK_NAME;
 | 
				
			||||||
 | 
						else if ((maxSizeBookName % 2))
 | 
				
			||||||
 | 
						    maxSizeBookName++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// std::cout << "  ID      Book      Expiration" << std::endl;
 | 
				
			||||||
 | 
						// std::cout << "------------------------------" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int fillID, fillBookName, fillExpiration=(20 - 10)/2;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						fillID = (ID_HASH_SIZE - 2) / 2;
 | 
				
			||||||
 | 
						fillBookName = (maxSizeBookName - 4) / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::cout.width (fillID);
 | 
				
			||||||
 | 
						std::cout << "";
 | 
				
			||||||
 | 
						std::cout << "ID" ;
 | 
				
			||||||
 | 
						std::cout.width (fillID);
 | 
				
			||||||
 | 
						std::cout << "";
 | 
				
			||||||
 | 
						std::cout << "    " ;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						std::cout.width (fillBookName);
 | 
				
			||||||
 | 
						std::cout << "";
 | 
				
			||||||
 | 
						std::cout << "Book" ;
 | 
				
			||||||
 | 
						std::cout.width (fillBookName);
 | 
				
			||||||
 | 
						std::cout << "";
 | 
				
			||||||
 | 
						std::cout << "    " ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::cout.width (fillExpiration);
 | 
				
			||||||
 | 
						std::cout << "";
 | 
				
			||||||
 | 
						std::cout << "Exipration";
 | 
				
			||||||
 | 
						std::cout.width (fillExpiration);
 | 
				
			||||||
 | 
						std::cout << "" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::cout.fill ('-');
 | 
				
			||||||
 | 
						std::cout.width (ID_HASH_SIZE + 4 + maxSizeBookName + 4 + 20);
 | 
				
			||||||
 | 
						std::cout << "" << std::endl;
 | 
				
			||||||
 | 
						std::cout.fill (' ');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string bookName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (const auto& kv : loanedBooks)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    loan = kv.second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    std::cout << kv.first;
 | 
				
			||||||
 | 
						    std::cout << "    ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    if (loan->bookName.size() > MAX_SIZE_BOOK_NAME)
 | 
				
			||||||
 | 
							bookName = std::string(loan->bookName.c_str(), MAX_SIZE_BOOK_NAME);
 | 
				
			||||||
 | 
						    else
 | 
				
			||||||
 | 
							bookName = loan->bookName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    std::cout << bookName;
 | 
				
			||||||
 | 
						    std::cout.width (maxSizeBookName - bookName.size());
 | 
				
			||||||
 | 
						    std::cout << "";
 | 
				
			||||||
 | 
						    std::cout << "    ";
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						    std::cout << loan->validity << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::cout << std::endl;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void returnBook(gourou::DRMProcessor& processor)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						struct Loan* loan = loanedBooks[std::string(returnID)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!loan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Error : Loan " << returnID << " doesn't exists" << std::endl;
 | 
				
			||||||
 | 
						    return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						processor.returnLoan(loan->id, loan->operatorURL);
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						deleteID = returnID;
 | 
				
			||||||
 | 
						if (deleteLoan(false))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Loan " << returnID << " successfully returned" << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    bool deleteLoan(bool displayResult=true)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						struct Loan* loan = loanedBooks[std::string(deleteID)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!loan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Error : Loan " << deleteID << " doesn't exists" << std::endl;
 | 
				
			||||||
 | 
						    return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (unlink(loan->path.c_str()))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Error : Cannot delete " << loan->path << std::endl;
 | 
				
			||||||
 | 
						    return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (displayResult)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Loan " << deleteID << " deleted" << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::map<std::string, struct Loan*> loanedBooks;
 | 
				
			||||||
 | 
					};	      
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void usage(const char* cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::cout << "Manage loaned books" << std::endl;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    std::cout << "Usage: " << cmd << " [(-d|--activation-dir) dir] (-l|--list)|(-D|--delete loanID)|(-R|--delete loanID) [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    std::cout << "  " << "-d|--activation-dir"  << "\t"   << "Directory of device.xml/activation.xml and device key" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-l|--list"            << "\t\t" << "List all loaned books" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-r|--return"          << "\t\t" << "Return a loaned book" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-D|--delete"          << "\t\t" << "Delete a loan entry without returning it" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-v|--verbose"         << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-V|--version"         << "\t\t" << "Display libgourou version" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  " << "-h|--help"            << "\t\t" << "This help" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::cout << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "Activation directory is optional. If not set, it's looked into :" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  * Current directory" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  * .adept" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  * adobe-digital-editions directory" << std::endl;
 | 
				
			||||||
 | 
					    std::cout << "  * .adobe-digital-editions directory" << std::endl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char** argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int c, ret = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
 | 
				
			||||||
 | 
					    int verbose = gourou::DRMProcessor::getLogLevel();
 | 
				
			||||||
 | 
					    int actions = 0;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
						int option_index = 0;
 | 
				
			||||||
 | 
						static struct option long_options[] = {
 | 
				
			||||||
 | 
						    {"activation-dir",   required_argument, 0,  'd' },
 | 
				
			||||||
 | 
						    {"list",             no_argument,       0,  'l' },
 | 
				
			||||||
 | 
						    {"return",           no_argument,       0,  'r' },
 | 
				
			||||||
 | 
						    {"delete",           no_argument,       0,  'D' },
 | 
				
			||||||
 | 
						    {"verbose",          no_argument,       0,  'v' },
 | 
				
			||||||
 | 
						    {"version",          no_argument,       0,  'V' },
 | 
				
			||||||
 | 
						    {"help",             no_argument,       0,  'h' },
 | 
				
			||||||
 | 
						    {0,                  0,                 0,  0 }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c = getopt_long(argc, argv, "d:lr:D:vVh",
 | 
				
			||||||
 | 
					                        long_options, &option_index);
 | 
				
			||||||
 | 
						if (c == -1)
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (c) {
 | 
				
			||||||
 | 
						case 'd':
 | 
				
			||||||
 | 
						    activationDir = optarg;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						case 'l':
 | 
				
			||||||
 | 
						    list = true;
 | 
				
			||||||
 | 
						    actions++;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						case 'r':
 | 
				
			||||||
 | 
						    returnID = optarg;
 | 
				
			||||||
 | 
						    actions++;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						case 'D':
 | 
				
			||||||
 | 
						    deleteID = optarg;
 | 
				
			||||||
 | 
						    actions++;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						case 'v':
 | 
				
			||||||
 | 
						    verbose++;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						case 'V':
 | 
				
			||||||
 | 
						    version();
 | 
				
			||||||
 | 
						    return 0;
 | 
				
			||||||
 | 
						case 'h':
 | 
				
			||||||
 | 
						    usage(argv[0]);
 | 
				
			||||||
 | 
						    return 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
						    usage(argv[0]);
 | 
				
			||||||
 | 
						    return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					    gourou::DRMProcessor::setLogLevel(verbose);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // By default, simply list books loaned
 | 
				
			||||||
 | 
					    if (actions == 0)
 | 
				
			||||||
 | 
						list = true;
 | 
				
			||||||
 | 
					    else if (actions != 1)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						usage(argv[0]);
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LoanMGT loanMGT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int i;
 | 
				
			||||||
 | 
					    bool hasErrors = false;
 | 
				
			||||||
 | 
					    const char* orig;
 | 
				
			||||||
 | 
					    char *filename;
 | 
				
			||||||
 | 
					    for (i=0; i<(int)ARRAY_SIZE(files); i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						orig = *files[i];
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						if (activationDir)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::string path = std::string(activationDir) + std::string("/") + orig;
 | 
				
			||||||
 | 
						    filename = strdup(path.c_str());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						    filename = strdup(orig);
 | 
				
			||||||
 | 
						*files[i] = findFile(filename);
 | 
				
			||||||
 | 
						free(filename);
 | 
				
			||||||
 | 
						if (!*files[i])
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl;
 | 
				
			||||||
 | 
						    hasErrors = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (hasErrors)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						// In case of activation dir was provided by user
 | 
				
			||||||
 | 
						activationDir = 0;
 | 
				
			||||||
 | 
						goto end;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (activationDir)
 | 
				
			||||||
 | 
						activationDir = strdup(activationDir); // For below free
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						activationDir = strdup(deviceFile);
 | 
				
			||||||
 | 
						activationDir = dirname(activationDir);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = loanMGT.run();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					end:
 | 
				
			||||||
 | 
					    for (i=0; i<(int)ARRAY_SIZE(files); i++)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						if (*files[i])
 | 
				
			||||||
 | 
						    free((void*)*files[i]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (activationDir)
 | 
				
			||||||
 | 
						free(activationDir);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -29,6 +29,9 @@
 | 
				
			|||||||
#ifndef _UTILS_COMMON_H_
 | 
					#ifndef _UTILS_COMMON_H_
 | 
				
			||||||
#define _UTILS_COMMON_H_
 | 
					#define _UTILS_COMMON_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOANS_DIR            "loans/"
 | 
				
			||||||
 | 
					#define ID_HASH_SIZE         16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
 | 
					#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user