reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

import.cpp (5540B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "import.hpp"
     19 
     20 #include "config.hpp"
     21 #include "download.hpp"
     22 #include "errors.hpp"
     23 #include "filesystem.hpp"
     24 #include "index.hpp"
     25 #include "reapack.hpp"
     26 #include "remote.hpp"
     27 #include "resource.hpp"
     28 #include "transaction.hpp"
     29 #include "win32.hpp"
     30 
     31 #include <boost/algorithm/string/trim.hpp>
     32 
     33 static const char *TITLE = "Import repositories";
     34 static const char *DISCOVER_URL = "https://reapack.com/repos";
     35 
     36 Import::Import()
     37   : Dialog(IDD_IMPORT_DIALOG), m_state(OK)
     38 {
     39 }
     40 
     41 void Import::onInit()
     42 {
     43   Dialog::onInit();
     44 
     45   Win32::setWindowText(handle(), TITLE);
     46 
     47   m_url = getControl(IDC_URL);
     48   m_progress = getControl(IDC_PROGRESS);
     49   m_discover = getControl(IDC_DISCOVER);
     50 
     51 #ifdef PBM_SETMARQUEE
     52   SendMessage(m_progress, PBM_SETMARQUEE, 1, 0);
     53 #endif
     54 }
     55 
     56 void Import::onCommand(const int id, int)
     57 {
     58   switch(id) {
     59   case IDOK:
     60     fetch();
     61     break;
     62   case IDC_DISCOVER:
     63     Win32::shellExecute(DISCOVER_URL);
     64     break;
     65   case IDCANCEL:
     66     if(m_pool) {
     67       disable(getControl(IDOK));
     68       disable(getControl(IDCANCEL));
     69       m_pool->abort();
     70       m_state = Close;
     71     }
     72     else
     73       close();
     74     break;
     75   }
     76 }
     77 
     78 void Import::onTimer(int)
     79 {
     80 #ifndef PBM_SETMARQUEE
     81   m_fakePos = (m_fakePos + 1) % 100;
     82   SendMessage(m_progress, PBM_SETPOS, m_fakePos, 0);
     83 #endif
     84 }
     85 
     86 ThreadPool *Import::setupPool()
     87 {
     88   if(!m_pool) {
     89     m_state = OK;
     90     m_pool = std::make_unique<ThreadPool>();
     91 
     92     m_pool->onAbort >> [=] { if(!m_state) m_state = Aborted; };
     93     m_pool->onDone >> [=] {
     94       setWaiting(false);
     95 
     96       if(!m_state)
     97         processQueue();
     98 
     99       m_queue.clear();
    100 
    101       if(m_state == Close)
    102         close();
    103       else
    104         SetFocus(m_url);
    105 
    106       m_pool.reset();
    107     };
    108   }
    109 
    110   return m_pool.get();
    111 }
    112 
    113 void Import::fetch()
    114 {
    115   if(m_pool) // ignore repeated presses on OK
    116     return;
    117 
    118   const auto &opts = g_reapack->config()->network;
    119 
    120   size_t index = 0;
    121   std::stringstream stream(Win32::getWindowText(m_url));
    122   std::string url;
    123   while(getline(stream, url)) {
    124     boost::algorithm::trim(url);
    125 
    126     if(url.empty())
    127       continue;
    128 
    129     MemoryDownload *dl = new MemoryDownload(url, opts);
    130     ++index;
    131 
    132     dl->onFinishAsync >> [=] {
    133       switch(dl->state()) {
    134       case ThreadTask::Success:
    135         // copy for later use, as `dl` won't be around after this callback
    136         if(!read(dl, index))
    137           m_pool->abort();
    138         break;
    139       case ThreadTask::Failure:
    140         Win32::messageBox(handle(), String::format(
    141           "Download failed: %s\n%s", dl->error().message.c_str(), url.c_str()
    142         ).c_str(), TITLE, MB_OK);
    143         m_pool->abort();
    144         break;
    145       default:
    146         break;
    147       }
    148     };
    149 
    150     setupPool()->push(dl);
    151   }
    152 
    153   if(m_pool)
    154     setWaiting(true);
    155   else
    156     close();
    157 }
    158 
    159 bool Import::read(MemoryDownload *dl, const size_t idx)
    160 try {
    161   IndexPtr index = Index::load({}, dl->contents().c_str());
    162   Remote remote = g_reapack->remote(index->name());
    163   const bool exists = !remote.isNull();
    164 
    165   if(exists && remote.url() != dl->url()) {
    166     if(remote.isProtected()) {
    167       Win32::messageBox(handle(), String::format(
    168         "The repository %s is protected and cannot be overwritten.",
    169         index->name().c_str()
    170       ).c_str(), TITLE, MB_OK);
    171       return false;
    172     }
    173     else {
    174       const auto answer = Win32::messageBox(handle(), String::format(
    175         "%s is already configured with a different URL.\n"
    176         "Do you want to overwrite it?", index->name().c_str()
    177       ).c_str(), TITLE, MB_YESNO);
    178 
    179       if(answer != IDYES)
    180         return true;
    181     }
    182   }
    183   else if(exists && remote.isEnabled()) // url is also the same
    184     return true; // nothing to do
    185 
    186   remote.setEnabled(true);
    187   remote.setName(index->name());
    188   remote.setUrl(dl->url());
    189   m_queue.push_back({idx, remote, dl->contents()});
    190 
    191   return true;
    192 }
    193 catch(const reapack_error &e) {
    194   Win32::messageBox(handle(), String::format(
    195     "The received file is invalid: %s\n%s", e.what(), dl->url().c_str()
    196   ).c_str(), TITLE, MB_OK);
    197   return false;
    198 }
    199 
    200 void Import::processQueue()
    201 {
    202   bool ok = true, commit = !m_queue.empty();
    203 
    204   sort(m_queue.begin(), m_queue.end());
    205 
    206   while(!m_queue.empty()) {
    207     if(!import(m_queue.front()))
    208       ok = false;
    209 
    210     m_queue.pop_front();
    211   }
    212 
    213   if(ok)
    214     m_state = Close;
    215 
    216   if(commit)
    217     g_reapack->commitConfig();
    218 }
    219 
    220 bool Import::import(const ImportData &data)
    221 {
    222   g_reapack->addSetRemote(data.remote);
    223 
    224   FS::write(Index::pathFor(data.remote.name()), data.contents);
    225 
    226   return true;
    227 }
    228 
    229 void Import::setWaiting(const bool wait)
    230 {
    231   setVisible(wait, m_progress);
    232   setVisible(!wait, m_discover);
    233   setEnabled(!wait, m_url);
    234 
    235 #ifndef PBM_SETMARQUEE
    236   const int timerId = 1;
    237 
    238   if(wait)
    239     startTimer(42, timerId);
    240   else
    241     stopTimer(timerId);
    242 
    243   m_fakePos = 0;
    244   SendMessage(m_progress, PBM_SETPOS, 0, 0);
    245 #endif
    246 }