1 /* 2 * Copyright 2017-2020, Andrew Lindesay <apl@lindesay.co.nz>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ServerRepositoryDataUpdateProcess.h" 8 9 #include <stdio.h> 10 #include <sys/stat.h> 11 #include <time.h> 12 13 #include <AutoDeleter.h> 14 #include <AutoLocker.h> 15 #include <Catalog.h> 16 #include <FileIO.h> 17 #include <Url.h> 18 19 #include "ServerSettings.h" 20 #include "StorageUtils.h" 21 #include "Logger.h" 22 #include "DumpExportRepository.h" 23 #include "DumpExportRepositorySource.h" 24 #include "DumpExportRepositoryJsonListener.h" 25 26 27 #undef B_TRANSLATION_CONTEXT 28 #define B_TRANSLATION_CONTEXT "ServerRepositoryDataUpdateProcess" 29 30 31 struct repository_and_repository_source { 32 DumpExportRepository* repository; 33 DumpExportRepositorySource* repositorySource; 34 }; 35 36 37 /*! This repository listener (not at the JSON level) is feeding in the 38 repositories as they are parsed and processing them. Processing 39 includes finding the matching depot record and coupling the data 40 from the server with the data about the depot. 41 */ 42 43 class DepotMatchingRepositoryListener : 44 public DumpExportRepositoryListener, public DepotMapper { 45 public: 46 DepotMatchingRepositoryListener(Model* model, 47 Stoppable* stoppable); 48 virtual ~DepotMatchingRepositoryListener(); 49 50 virtual DepotInfo MapDepot(const DepotInfo& depot, void *context); 51 virtual bool Handle(DumpExportRepository* item); 52 void Handle(repository_and_repository_source& pair); 53 void Handle(const BString& identifier, 54 repository_and_repository_source& pair); 55 virtual void Complete(); 56 57 private: 58 void NormalizeUrl(BUrl& url) const; 59 bool IsUnassociatedDepotByUrl( 60 const DepotInfo& depotInfo, 61 const BString& urlStr) const; 62 63 int32 IndexOfUnassociatedDepotByUrl( 64 const BString& url) const; 65 66 Model* fModel; 67 Stoppable* fStoppable; 68 }; 69 70 71 DepotMatchingRepositoryListener::DepotMatchingRepositoryListener( 72 Model* model, Stoppable* stoppable) 73 : 74 fModel(model), 75 fStoppable(stoppable) 76 { 77 } 78 79 80 DepotMatchingRepositoryListener::~DepotMatchingRepositoryListener() 81 { 82 } 83 84 85 /*! This is invoked as a result of logic in 'Handle(..)' that requests that the 86 model call this method with the requested DepotInfo instance. 87 */ 88 89 DepotInfo 90 DepotMatchingRepositoryListener::MapDepot(const DepotInfo& depot, void *context) 91 { 92 repository_and_repository_source* repositoryAndRepositorySource = 93 (repository_and_repository_source*) context; 94 BString* repositoryCode = 95 repositoryAndRepositorySource->repository->Code(); 96 BString* repositorySourceCode = 97 repositoryAndRepositorySource->repositorySource->Code(); 98 99 DepotInfo modifiedDepotInfo(depot); 100 modifiedDepotInfo.SetWebAppRepositoryCode(BString(*repositoryCode)); 101 modifiedDepotInfo.SetWebAppRepositorySourceCode( 102 BString(*repositorySourceCode)); 103 104 if (Logger::IsDebugEnabled()) { 105 HDDEBUG("[DepotMatchingRepositoryListener] associated depot [%s] (%s) " 106 "with server repository source [%s] (%s)", 107 modifiedDepotInfo.Name().String(), 108 modifiedDepotInfo.URL().String(), 109 repositorySourceCode->String(), 110 repositoryAndRepositorySource 111 ->repositorySource->Identifier()->String()); 112 } else { 113 HDINFO("[DepotMatchingRepositoryListener] associated depot [%s] with " 114 "server repository source [%s]", 115 modifiedDepotInfo.Name().String(), 116 repositorySourceCode->String()); 117 } 118 119 return modifiedDepotInfo; 120 } 121 122 123 void 124 DepotMatchingRepositoryListener::Handle(const BString& identifier, 125 repository_and_repository_source& pair) 126 { 127 if (!identifier.IsEmpty()) { 128 fModel->ReplaceDepotByIdentifier(identifier, this, &pair); 129 } 130 } 131 132 133 void 134 DepotMatchingRepositoryListener::Handle(repository_and_repository_source& pair) 135 { 136 if (!pair.repositorySource->IdentifierIsNull()) 137 Handle(*(pair.repositorySource->Identifier()), pair); 138 139 // there may be additional identifiers for the remote repository and 140 // these should also be taken into consideration. 141 142 for(int32 i = 0; 143 i < pair.repositorySource->CountExtraIdentifiers(); 144 i++) 145 { 146 Handle(*(pair.repositorySource->ExtraIdentifiersItemAt(i)), pair); 147 } 148 } 149 150 151 bool 152 DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository) 153 { 154 int32 i; 155 156 for (i = 0; i < repository->CountRepositorySources(); i++) { 157 repository_and_repository_source repositoryAndRepositorySource; 158 repositoryAndRepositorySource.repository = repository; 159 repositoryAndRepositorySource.repositorySource = 160 repository->RepositorySourcesItemAt(i); 161 Handle(repositoryAndRepositorySource); 162 } 163 164 return !fStoppable->WasStopped(); 165 } 166 167 168 void 169 DepotMatchingRepositoryListener::Complete() 170 { 171 } 172 173 174 ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess( 175 Model* model, 176 uint32 serverProcessOptions) 177 : 178 AbstractSingleFileServerProcess(serverProcessOptions), 179 fModel(model) 180 { 181 } 182 183 184 ServerRepositoryDataUpdateProcess::~ServerRepositoryDataUpdateProcess() 185 { 186 } 187 188 189 const char* 190 ServerRepositoryDataUpdateProcess::Name() const 191 { 192 return "ServerRepositoryDataUpdateProcess"; 193 } 194 195 196 const char* 197 ServerRepositoryDataUpdateProcess::Description() const 198 { 199 return B_TRANSLATE("Synchronizing meta-data about repositories"); 200 } 201 202 203 BString 204 ServerRepositoryDataUpdateProcess::UrlPathComponent() 205 { 206 BString result; 207 AutoLocker<BLocker> locker(fModel->Lock()); 208 result.SetToFormat("/__repository/all-%s.json.gz", 209 fModel->Language()->PreferredLanguage()->Code()); 210 return result; 211 } 212 213 214 status_t 215 ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const 216 { 217 AutoLocker<BLocker> locker(fModel->Lock()); 218 return fModel->DumpExportRepositoryDataPath(path); 219 } 220 221 222 status_t 223 ServerRepositoryDataUpdateProcess::ProcessLocalData() 224 { 225 DepotMatchingRepositoryListener* itemListener = 226 new DepotMatchingRepositoryListener(fModel, this); 227 ObjectDeleter<DepotMatchingRepositoryListener> 228 itemListenerDeleter(itemListener); 229 230 BulkContainerDumpExportRepositoryJsonListener* listener = 231 new BulkContainerDumpExportRepositoryJsonListener(itemListener); 232 ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener> 233 listenerDeleter(listener); 234 235 BPath localPath; 236 status_t result = GetLocalPath(localPath); 237 238 if (result != B_OK) 239 return result; 240 241 result = ParseJsonFromFileWithListener(listener, localPath); 242 243 if (result != B_OK) 244 return result; 245 246 return listener->ErrorStatus(); 247 } 248 249 250 status_t 251 ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const 252 { 253 return GetLocalPath(path); 254 } 255 256 257 void 258 ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath( 259 BString& jsonPath) const 260 { 261 jsonPath.SetTo("$.info"); 262 } 263