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 }