aboutsummaryrefslogtreecommitdiff
path: root/src/lib/mods.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/mods.cc')
-rw-r--r--src/lib/mods.cc233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/lib/mods.cc b/src/lib/mods.cc
new file mode 100644
index 0000000..ab54e48
--- /dev/null
+++ b/src/lib/mods.cc
@@ -0,0 +1,233 @@
+#include "libbible.h"
+#include <sword/swmgr.h>
+#include <sword/swmodule.h>
+#include <sword/installmgr.h>
+#include <sword/filemgr.h>
+#include <sword/remotetrans.h>
+#include <unzip.h>
+#include <filesystem>
+
+using namespace std;
+
+class myStatusReporter : public sword::StatusReporter {
+ public:
+ myStatusReporter(libbible::Status *status);
+ ~myStatusReporter();
+ void preStatus(long totalBytes, long completedBytes, const char *message);
+ void update(unsigned long totalBytes, unsigned long completedBytes);
+ protected:
+ libbible::Status *status;
+ string message;
+};
+
+myStatusReporter::myStatusReporter(libbible::Status *s) {
+ status = s;
+}
+
+myStatusReporter::~myStatusReporter() {};
+
+//virtual void libbible::Status::update(unsigned long totalBytes, unsigned long completedBytes, string message) {}
+
+void myStatusReporter::preStatus(long totalBytes, long completedBytes, const char *msg) {
+ message = string(msg);
+ status->update((unsigned long) totalBytes, (unsigned long) completedBytes, message);
+ //printf("Got a status update: %ld / %ld, \"%s\"\n", completedBytes, totalBytes, message.c_str());
+}
+
+void myStatusReporter::update(unsigned long totalBytes, unsigned long completedBytes) {
+ status->update(totalBytes, completedBytes, message);
+ //printf("Got a status update: %ld / %ld, \"%s\"\n", completedBytes, totalBytes, message.c_str());
+}
+
+string basedir = (getenv("HOME")) + string("/.sword/");
+sword::InstallMgr *installMgr = new sword::InstallMgr((basedir + std::string("InstallMgr")).c_str(), nullptr);
+map<string, vector<pair<string, sword::InstallSource *>>> installSources;
+map<string, string> languageNames; // maps abbreviation to full name
+
+void libbible::setStatusReporter(libbible::Status& status) {
+ myStatusReporter *msr = new myStatusReporter(&status);
+ free(installMgr);
+ installMgr = new sword::InstallMgr((basedir + std::string("InstallMgr")).c_str(), msr);
+ installMgr->setUserDisclaimerConfirmed(true);
+}
+
+map<string, vector<string>> libbible::downloadModsAvailable() {
+ installSources.clear();
+ languageNames.clear();
+ mkdir((basedir + std::string("mods.d/")).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ mkdir((basedir + std::string("modules/")).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ installMgr->setUserDisclaimerConfirmed(true);
+ string confpath = basedir + string("InstallMgr/InstallMgr.conf");
+ if(! sword::FileMgr::existsFile(confpath.c_str())) {
+ // Lifted directly from xiphos
+ sword::FileMgr::createParent(confpath.c_str());
+ sword::SWConfig config(confpath.c_str());
+ sword::InstallSource is("FTP");
+ is.caption = "CrossWire";
+ is.source = "ftp.crosswire.org";
+ is.directory = "/pub/sword/raw";
+ config["General"]["PassiveFTP"] = "true";
+ config["Sources"]["FTPSource"] = is.getConfEnt();
+ config.save();
+ installMgr->refreshRemoteSourceConfiguration();
+ }
+ installMgr->readInstallConf();
+ map<string, vector<string>> modsAvailable;
+ map<string, vector<string>> languagesToFull;
+ //printf("Getting langs...\n");
+ for(auto src : installMgr->sources) {
+ if(src.second->getMgr()->Modules.empty()) {
+ //printf("Refreshing remote source: %s\n", src.second->getConfEnt().c_str());
+ installMgr->refreshRemoteSource(src.second);
+ }
+ for(auto mod : src.second->getMgr()->Modules) {
+ auto *curMod = mod.second;
+ string type(curMod->getType());
+ if(type == "Biblical Texts") {
+ string language(curMod->getLanguage());
+ string fullLang;
+ if(curMod->getConfigEntry("LCSH")) {
+ // Split on periods, last field, strip
+ fullLang = string(curMod->getConfigEntry("LCSH"));
+ // If ends with ., remove
+ if(fullLang.ends_with('.')) fullLang = fullLang.substr(0, fullLang.size()-1);
+ if(fullLang.find('.') != string::npos) fullLang = fullLang.substr(fullLang.find_last_of('.')+1);
+ while(fullLang.starts_with(' ')) fullLang = fullLang.substr(1);
+ while(fullLang.ends_with(' ')) fullLang = fullLang.substr(0, fullLang.size()-1);
+ }
+ vector<string> newLangs;
+ languagesToFull.emplace(language, newLangs);
+ languagesToFull[language].push_back(fullLang);
+ vector<string> newMods;
+ vector<pair<string, sword::InstallSource *>> newSources;
+ // emplace only adds if key is unique
+ modsAvailable.emplace(language, newMods);
+ installSources.emplace(language, newSources);
+ modsAvailable[language].push_back(string(curMod->getName()));
+ pair<string, sword::InstallSource *> p(string(curMod->getName()), src.second);
+ installSources[language].push_back(p);
+ }
+ }
+ }
+ // Now use majority voting to move languagesToFull -> languageNames
+ for(const auto& [abbrev, fulls] : languagesToFull) {
+ std::map<string, int> majVote;
+ for(auto full : fulls) {
+ majVote.try_emplace(full, 0);
+ majVote[full]++;
+ }
+ string selected = fulls[0];
+ for(auto full : fulls) {
+ if(majVote[full] > majVote[selected] or (majVote[full] == majVote[selected] and !full.empty() and full.size() < selected.size())) {
+ selected = full;
+ }
+ }
+ if(selected.empty()) languageNames[abbrev] = abbrev;
+ else languageNames[abbrev] = selected;
+ }
+ return modsAvailable;
+}
+
+std::map<std::string, std::string> libbible::getLanguageNames() {
+ if(languageNames.empty()) {
+ downloadModsAvailable();
+ }
+ return languageNames;
+}
+
+void libbible::terminateDownload() {
+ installMgr->terminate();
+}
+
+bool libbible::installModFromInternet(string language, string name) {
+ // Searching through map<string, vector<pair<string, sword::InstallSource *>>> installSources;
+ if(installSources.empty()) {
+ downloadModsAvailable();
+ }
+ for (pair<string, sword::InstallSource *> p : installSources[language]) {
+ if(p.first == name) {
+ sword::SWMgr mgr(basedir.c_str());
+ if(installMgr->installModule(&mgr, 0, name.c_str(), p.second) == 0) {
+ printf("Installed from %s\n", p.second->getConfEnt().c_str());
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+#define READ_SIZE 8192
+#define delim '/'
+
+bool libbible::installModFromZip(string filename) {
+ // So... turns out it's a mite unsupported to install from a .zip
+ // Here's the deal. We do a syscall to unzip. We fancy like that.
+ // TODO: Use the ZipCompress module from SWORD instead.
+ /*string command = "unzip -o " + filename + " -d " + basedir + "&> /dev/null";
+ if(system(command.c_str())) {
+ //Uh oh...
+ printf("Something bad happened when unpacking %s\n. Is unzip installed?", filename.c_str());
+ }*/
+ unzFile zipfile = unzOpen(filename.c_str());
+ if(zipfile == NULL) {
+ return false;
+ }
+ unz_global_info global_info;
+ if(unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK) {
+ unzClose(zipfile);
+ return false;
+ }
+ char read_buffer[READ_SIZE];
+ ulong i;
+ for(i = 0; i < global_info.number_entry; i++) {
+ unz_file_info file_info;
+ if(unzGetCurrentFileInfo(zipfile, &file_info, read_buffer, READ_SIZE, NULL, 0, NULL, 0) != UNZ_OK) {
+ unzClose(zipfile);
+ return false;
+ }
+ string fname = basedir + string(read_buffer);
+ size_t pos = fname.find_last_of(delim);
+ if(pos != string::npos) {
+ string path = fname.substr(0, pos);
+ filesystem::create_directories(path);
+ }
+ if(unzOpenCurrentFile(zipfile) != UNZ_OK) {
+ unzCloseCurrentFile(zipfile);
+ unzClose(zipfile);
+ return false;
+ }
+ FILE *out = fopen(fname.c_str(), "wb");
+ if(out == NULL) {
+ unzCloseCurrentFile(zipfile);
+ unzClose(zipfile);
+ return false;
+ }
+ int bytesRead;
+ do {
+ bytesRead = unzReadCurrentFile(zipfile, read_buffer, READ_SIZE);
+ if(bytesRead < 0) {
+ printf("error %d\n", bytesRead);
+ unzCloseCurrentFile(zipfile);
+ unzClose(zipfile);
+ return false;
+ }
+ if(bytesRead > 0) {
+ fwrite(read_buffer, bytesRead, 1, out);
+ }
+ } while(bytesRead > 0);
+ fclose(out);
+ unzCloseCurrentFile(zipfile);
+ unzGoToNextFile(zipfile);
+ }
+ unzClose(zipfile);
+ return true;
+}
+
+void libbible::uninstallMod(string modname) {
+ sword::SWMgr mgr(basedir.c_str());
+ sword::ModMap::iterator it = mgr.Modules.find(modname.c_str());
+ if(it != mgr.Modules.end()) {
+ installMgr->removeModule(&mgr, it->second->getName());
+ }
+}