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 printf("[DepotMatchingRepositoryListener] associated depot [%s] (%s) " 106 "with server repository source [%s] (%s)\n", 107 modifiedDepotInfo.Name().String(), 108 modifiedDepotInfo.URL().String(), 109 repositorySourceCode->String(), 110 repositoryAndRepositorySource 111 ->repositorySource->Identifier()->String()); 112 } else { 113 printf("[DepotMatchingRepositoryListener] associated depot [%s] with " 114 "server repository source [%s]\n", 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 Handle(*(pair.repositorySource->Identifier()), pair); 137 138 // there may be additional identifiers for the remote repository and 139 // these should also be taken into consideration. 140 141 for(int32 i = 0; 142 i < pair.repositorySource->CountExtraIdentifiers(); 143 i++) 144 { 145 Handle(*(pair.repositorySource->ExtraIdentifiersItemAt(i)), pair); 146 } 147 } 148 149 150 bool 151 DepotMatchingRepositoryListener::Handle(DumpExportRepository* repository) 152 { 153 int32 i; 154 155 for (i = 0; i < repository->CountRepositorySources(); i++) { 156 repository_and_repository_source repositoryAndRepositorySource; 157 repositoryAndRepositorySource.repository = repository; 158 repositoryAndRepositorySource.repositorySource = 159 repository->RepositorySourcesItemAt(i); 160 Handle(repositoryAndRepositorySource); 161 } 162 163 return !fStoppable->WasStopped(); 164 } 165 166 167 void 168 DepotMatchingRepositoryListener::Complete() 169 { 170 } 171 172 173 ServerRepositoryDataUpdateProcess::ServerRepositoryDataUpdateProcess( 174 Model* model, 175 uint32 serverProcessOptions) 176 : 177 AbstractSingleFileServerProcess(serverProcessOptions), 178 fModel(model) 179 { 180 } 181 182 183 ServerRepositoryDataUpdateProcess::~ServerRepositoryDataUpdateProcess() 184 { 185 } 186 187 188 const char* 189 ServerRepositoryDataUpdateProcess::Name() const 190 { 191 return "ServerRepositoryDataUpdateProcess"; 192 } 193 194 195 const char* 196 ServerRepositoryDataUpdateProcess::Description() const 197 { 198 return B_TRANSLATE("Synchronizing meta-data about repositories"); 199 } 200 201 202 BString 203 ServerRepositoryDataUpdateProcess::UrlPathComponent() 204 { 205 BString result; 206 AutoLocker<BLocker> locker(fModel->Lock()); 207 result.SetToFormat("/__repository/all-%s.json.gz", 208 fModel->Language().PreferredLanguage()->Code()); 209 return result; 210 } 211 212 213 status_t 214 ServerRepositoryDataUpdateProcess::GetLocalPath(BPath& path) const 215 { 216 AutoLocker<BLocker> locker(fModel->Lock()); 217 return fModel->DumpExportRepositoryDataPath(path); 218 } 219 220 221 status_t 222 ServerRepositoryDataUpdateProcess::ProcessLocalData() 223 { 224 DepotMatchingRepositoryListener* itemListener = 225 new DepotMatchingRepositoryListener(fModel, this); 226 ObjectDeleter<DepotMatchingRepositoryListener> 227 itemListenerDeleter(itemListener); 228 229 BulkContainerDumpExportRepositoryJsonListener* listener = 230 new BulkContainerDumpExportRepositoryJsonListener(itemListener); 231 ObjectDeleter<BulkContainerDumpExportRepositoryJsonListener> 232 listenerDeleter(listener); 233 234 BPath localPath; 235 status_t result = GetLocalPath(localPath); 236 237 if (result != B_OK) 238 return result; 239 240 result = ParseJsonFromFileWithListener(listener, localPath); 241 242 if (result != B_OK) 243 return result; 244 245 return listener->ErrorStatus(); 246 } 247 248 249 status_t 250 ServerRepositoryDataUpdateProcess::GetStandardMetaDataPath(BPath& path) const 251 { 252 return GetLocalPath(path); 253 } 254 255 256 void 257 ServerRepositoryDataUpdateProcess::GetStandardMetaDataJsonPath( 258 BString& jsonPath) const 259 { 260 jsonPath.SetTo("$.info"); 261 } 262