]> git.mxchange.org Git - simgear.git/blob - simgear/io/SVNDirectory.cxx
Lots of (mostly) doxygen fixes/cleanup.
[simgear.git] / simgear / io / SVNDirectory.cxx
1
2 #include "SVNDirectory.hxx"
3
4 #include <cassert>
5 #include <fstream>
6 #include <iostream>
7 #include <boost/foreach.hpp>
8
9 #include <simgear/debug/logstream.hxx>
10 #include <simgear/misc/strutils.hxx>
11 #include <simgear/misc/sg_dir.hxx>
12 #include <simgear/io/HTTPClient.hxx>
13 #include <simgear/io/DAVMultiStatus.hxx>
14 #include <simgear/io/SVNRepository.hxx>
15 #include <simgear/io/sg_file.hxx>
16 #include <simgear/io/SVNReportParser.hxx>
17 #include <simgear/package/md5.h>
18 #include <simgear/structure/exception.hxx>
19
20 using std::string;
21 using std::cout;
22 using std::endl;
23 using namespace simgear;
24
25 typedef std::vector<HTTP::Request_ptr> RequestVector;
26 typedef std::map<std::string, DAVResource*> DAVResourceMap;
27
28
29 const char* DAV_CACHE_NAME = ".terrasync_cache";
30 const char* CACHE_VERSION_4_TOKEN = "terrasync-cache-4";
31
32 // important: with the Google servers, setting this higher than '1' causes
33 // server internal errors (500, the connection is closed). In other words we
34 // can only specify update report items one level deep at most and no more.
35 // (the root and its direct children, not NOT grand-children)
36 const unsigned int MAX_UPDATE_REPORT_DEPTH = 1;
37
38 enum LineState
39 {
40   LINESTATE_HREF = 0,
41   LINESTATE_VERSIONNAME
42 };
43
44 SVNDirectory::SVNDirectory(SVNRepository *r, const SGPath& path) :
45   localPath(path),
46   dav(NULL),
47   repo(r),
48   _doingUpdateReport(false),
49   _parent(NULL)
50 {
51   if (path.exists()) {
52     parseCache();
53   } 
54   
55   // don't create dir here, repo might not exist at all
56 }
57   
58 SVNDirectory::SVNDirectory(SVNDirectory* pr, DAVCollection* col) :
59   dav(col),
60   repo(pr->repository()),
61   _doingUpdateReport(false),
62   _parent(pr)
63 {
64   assert(col->container());
65   assert(!col->url().empty());
66   assert(_parent);
67
68   localPath = pr->fsDir().file(col->name());
69   if (!localPath.exists()) {
70     Dir d(localPath);
71     d.create(0755);
72     writeCache();
73   } else {
74     parseCache();
75   }
76 }
77
78 SVNDirectory::~SVNDirectory()
79 {
80     // recursive delete our child directories
81     BOOST_FOREACH(SVNDirectory* d, _children) {
82         delete d;
83     }
84 }
85
86 void SVNDirectory::parseCache()
87 {
88   SGPath p(localPath);
89   p.append(DAV_CACHE_NAME);
90   if (!p.exists()) {
91     return;
92   }
93     
94   char href[1024];
95   char versionName[128];
96   LineState lineState = LINESTATE_HREF;
97   std::ifstream file(p.c_str());
98     if (!file.is_open()) {
99         SG_LOG(SG_IO, SG_WARN, "unable to open cache file for reading:" << p);
100         return;
101     }
102   bool doneSelf = false;
103     
104   file.getline(href, 1024);
105   if (strcmp(CACHE_VERSION_4_TOKEN, href)) {
106     SG_LOG(SG_IO, SG_WARN, "invalid cache file [missing header token]:" << p << " '" << href << "'");
107     return;
108   }
109     
110     std::string vccUrl;
111     file.getline(href, 1024);
112     vccUrl = href;
113     
114   while (!file.eof()) {
115     if (lineState == LINESTATE_HREF) {
116       file.getline(href, 1024);
117       lineState = LINESTATE_VERSIONNAME;
118     } else {
119       assert(lineState == LINESTATE_VERSIONNAME);
120       file.getline(versionName, 1024);
121       lineState = LINESTATE_HREF;
122       char* hrefPtr = href;
123     
124       if (!doneSelf) {
125         if (!dav) {
126           dav = new DAVCollection(hrefPtr);
127           dav->setVersionName(versionName);
128         } else {
129           assert(string(hrefPtr) == dav->url());
130         }
131         
132         if (!vccUrl.empty()) {
133             dav->setVersionControlledConfiguration(vccUrl);
134         }
135         
136         _cachedRevision = versionName;
137         doneSelf = true;
138       } else {
139         DAVResource* child = addChildDirectory(hrefPtr)->collection();
140           string s = strutils::strip(versionName);
141           if (!s.empty()) {
142               child->setVersionName(versionName);
143           }
144       } // of done self test
145     } // of line-state switching 
146   } // of file get-line loop
147 }
148   
149 void SVNDirectory::writeCache()
150 {
151   SGPath p(localPath);
152   if (!p.exists()) {
153       Dir d(localPath);
154       d.create(0755);
155   }
156   
157   p.append(string(DAV_CACHE_NAME) + ".new");
158     
159   std::ofstream file(p.c_str(), std::ios::trunc);
160 // first, cache file version header
161   file << CACHE_VERSION_4_TOKEN << '\n';
162  
163 // second, the repository VCC url
164   file << dav->versionControlledConfiguration() << '\n';
165       
166 // third, our own URL, and version
167   file << dav->url() << '\n' << _cachedRevision << '\n';
168   
169   BOOST_FOREACH(DAVResource* child, dav->contents()) {
170     if (child->isCollection()) {
171         file << child->name() << '\n' << child->versionName() << "\n";
172     }
173   } // of child iteration
174     
175     file.close();
176     
177 // approximately atomic delete + rename operation
178     SGPath cacheName(localPath);
179     cacheName.append(DAV_CACHE_NAME);
180     p.rename(cacheName);
181 }
182
183 void SVNDirectory::setBaseUrl(const string& url)
184 {
185     if (_parent) {
186         SG_LOG(SG_IO, SG_ALERT, "setting base URL on non-root directory " << url);
187         return;
188     }
189     
190     if (dav && (url == dav->url())) {
191         return;
192     }
193     
194     dav = new DAVCollection(url);
195 }
196
197 std::string SVNDirectory::url() const
198 {
199   if (!_parent) {
200     return repo->baseUrl();
201   }
202   
203   return _parent->url() + "/" + name();
204 }
205
206 std::string SVNDirectory::name() const
207 {
208     return dav->name();
209
210
211 DAVResource*
212 SVNDirectory::addChildFile(const std::string& fileName)
213 {
214     DAVResource* child = NULL;
215     child = new DAVResource(dav->urlForChildWithName(fileName));
216     dav->addChild(child);
217   
218     writeCache();
219     return child;
220 }
221
222 SVNDirectory*
223 SVNDirectory::addChildDirectory(const std::string& dirName)
224 {
225     if (dav->childWithName(dirName)) {
226         // existing child, let's remove it
227         deleteChildByName(dirName);
228     }
229     
230     DAVCollection* childCol = dav->createChildCollection(dirName);
231     SVNDirectory* child = new SVNDirectory(this, childCol);
232     childCol->setVersionName(child->cachedRevision());
233     _children.push_back(child);
234     writeCache();
235     return child;
236 }
237
238 void SVNDirectory::deleteChildByName(const std::string& nm)
239 {
240     DAVResource* child = dav->childWithName(nm);
241     if (!child) {
242 //        std::cerr << "ZZZ: deleteChildByName: unknown:" << nm << std::endl;
243         return;
244     }
245
246     SGPath path = fsDir().file(nm);
247     
248     if (child->isCollection()) {
249         Dir d(path);
250         d.remove(true);
251     
252         DirectoryList::iterator it = findChildDir(nm);
253         if (it != _children.end()) {
254             SVNDirectory* c = *it;
255     //        std::cout << "YYY: deleting SVNDirectory for:" << nm << std::endl;
256             delete c;
257             _children.erase(it);
258         }
259     } else {
260         path.remove();
261     }
262
263     dav->removeChild(child);
264     delete child;
265
266     writeCache();
267 }
268   
269 bool SVNDirectory::isDoingSync() const
270 {
271   if (_doingUpdateReport) {
272       return true;
273   } 
274
275   BOOST_FOREACH(SVNDirectory* child, _children) {
276       if (child->isDoingSync()) {
277           return true;
278       } // of children
279   }
280     
281   return false;
282 }
283
284 void SVNDirectory::beginUpdateReport()
285 {
286     _doingUpdateReport = true;
287     _cachedRevision.clear();
288     writeCache();
289 }
290
291 void SVNDirectory::updateReportComplete()
292 {
293     _cachedRevision = dav->versionName();
294     _doingUpdateReport = false;
295     writeCache();
296     
297     SVNDirectory* pr = parent();
298     if (pr) {
299         pr->writeCache();
300     }    
301 }
302
303 SVNRepository* SVNDirectory::repository() const
304 {
305     return repo;
306 }
307
308 void SVNDirectory::mergeUpdateReportDetails(unsigned int depth, 
309     string_list& items)
310 {
311     // normal, easy case: we are fully in-sync at a revision
312     if (!_cachedRevision.empty()) {
313         std::ostringstream os;
314         os << "<S:entry rev=\"" << _cachedRevision << "\" depth=\"infinity\">"
315             << repoPath() << "</S:entry>";
316         items.push_back(os.str());
317         return;
318     }
319     
320     Dir d(localPath);
321     if (depth >= MAX_UPDATE_REPORT_DEPTH) {
322         d.removeChildren();
323         return;
324     }
325     
326     PathList cs = d.children(Dir::NO_DOT_OR_DOTDOT | Dir::INCLUDE_HIDDEN | Dir::TYPE_DIR);
327     BOOST_FOREACH(SGPath path, cs) {
328         SVNDirectory* c = child(path.file());
329         if (!c) {
330             // ignore this child, if it's an incomplete download,
331             // it will be over-written on the update anyway
332             //std::cerr << "unknown SVN child" << path << std::endl;
333         } else {
334             // recurse down into children
335             c->mergeUpdateReportDetails(depth+1, items);
336         }
337     } // of child dir iteration
338 }
339
340 std::string SVNDirectory::repoPath() const
341 {
342     if (!_parent) {
343         return "/";
344     }
345     
346     // find the length of the repository base URL, then
347     // trim that off our repo URL - job done!
348     size_t baseUrlLen = repo->baseUrl().size();
349     return dav->url().substr(baseUrlLen + 1);
350 }
351
352 SVNDirectory* SVNDirectory::parent() const
353 {
354     return _parent;
355 }
356
357 SVNDirectory* SVNDirectory::child(const std::string& dirName) const
358 {
359     BOOST_FOREACH(SVNDirectory* d, _children) {
360         if (d->name() == dirName) {
361             return d;
362         }
363     }
364     
365     return NULL;
366 }
367
368 DirectoryList::iterator
369 SVNDirectory::findChildDir(const std::string& dirName)
370 {
371     DirectoryList::iterator it;
372     for (it=_children.begin(); it != _children.end(); ++it) {
373         if ((*it)->name() == dirName) {
374             return it;
375         }
376     }
377     return it;
378 }
379
380 simgear::Dir SVNDirectory::fsDir() const
381 {
382     return Dir(localPath);
383 }