1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <kfg/kfg-priv.h> /* KConfigFixMainResolverCgiNode */
28 
29 #include <klib/printf.h> /* string_printf */
30 #include <klib/strings.h> /* SDL_CGI */
31 
32 #include "util.hpp" // CStdIn
33 
34 #include <sstream> // ostringstream
35 
36 #include <climits> /* PATH_MAX */
37 #ifndef PATH_MAX
38 #define PATH_MAX 4096
39 #endif
40 
41 using std::string;
42 
Read(char * buffer,size_t bsize,size_t * num_read)43 rc_t CStdIn::Read(char *buffer, size_t bsize, size_t *num_read) {
44     size_t dummy = 0;
45     if (num_read == NULL) {
46 if (DEBUG) OUTMSG(("<<< Read: num_read == NULL\n"));
47         num_read = &dummy;
48     }
49 
50     if (bsize == 0) {
51         *num_read = 0;
52         return 0;
53     }
54 
55     rc_t rc = KFileRead(m_Self, m_Pos, buffer, bsize, num_read);
56     if (rc == 0) {
57         m_Pos += *num_read;
58         size_t last = *num_read;
59         if (*num_read >= bsize) {
60             last = bsize - 1;
61         }
62 
63         while (true) {
64             buffer[last] = '\0';
65             if (last == 0) {
66                 break;
67             }
68             --last;
69             if (buffer[last] != '\n' && buffer[last] != '\r') {
70                 break;
71             }
72             --*num_read;
73         }
74     }
75 
76 if (DEBUG) OUTMSG(("<<< Read: num_read = %d\n", *num_read));
77 
78 return rc;
79 }
80 
CanWriteFile(const CString & dir,bool verbose) const81 rc_t CKDirectory::CanWriteFile(const CString &dir, bool verbose) const {
82     bool ok = true;
83     rc_t rc = 0;
84     char path[PATH_MAX] = "";
85     if (verbose) {
86         OUTMSG(("checking whether %S is writable... ", dir.Get()));
87     }
88     for (int i = 0; i < 10000 && rc == 0; ++i) {
89         size_t path_len = 0;
90         rc = string_printf(path, sizeof path, &path_len,
91             "%S/.tmp%d.tmp", dir.Get(), i);
92         if (rc != 0) {
93             break;
94         }
95         assert(path_len <= sizeof path);
96         if (Exists(path)) {
97             KDirectoryRemove(m_Self, false, path);
98         }
99         else {
100             KFile *f = NULL;
101             rc = KDirectoryCreateFile(m_Self,
102                 &f, false, m_PrivateAccess, kcmCreate, path);
103             if (rc == 0) {
104                 rc = KFileWrite(f, 0, path, path_len, NULL);
105             }
106             RELEASE(KFile, f);
107             const KFile *cf = NULL;
108             if (rc == 0) {
109                 rc = KDirectoryOpenFileRead(m_Self, &cf, path);
110             }
111             char buffer[PATH_MAX] = "";
112             size_t num_read = 0;
113             if (rc == 0) {
114                 rc = KFileRead(cf, 0, buffer, sizeof buffer, &num_read);
115             }
116             if (rc == 0) {
117                 if (path_len != num_read || string_cmp(path,
118                     path_len, buffer, num_read, sizeof buffer) != 0)
119                 {
120                     if (verbose) {
121                         OUTMSG(("no\n"));
122                     }
123                     OUTMSG(("Warning: "
124                         "NCBI Home directory is not writable"));
125                     ok = false;
126                 }
127             }
128             RELEASE(KFile, cf);
129             if (rc == 0) {
130                 KDirectoryRemove(m_Self, false, path);
131             }
132             break;
133         }
134     }
135     if (verbose && ok) {
136         if (rc == 0) {
137             OUTMSG(("yes\n"));
138         }
139         else {
140             OUTMSG(("failed\n"));
141         }
142     }
143     return rc;
144 }
145 
CheckAccess(const CString & path,bool & updated,bool isPrivate,bool verbose) const146 rc_t CKDirectory::CheckAccess(const CString &path,
147     bool &updated, bool isPrivate, bool verbose) const
148 {
149     updated = false;
150     const String *str = path.Get();
151     if (str == NULL) {
152         return 0;
153     }
154     uint32_t access = 0;
155     if (verbose) {
156         OUTMSG(("checking %S file mode... ", path.Get()));
157     }
158     rc_t rc = KDirectoryAccess(m_Self, &access, str->addr);
159     if (rc != 0) {
160         OUTMSG(("failed\n"));
161     }
162     else {
163         if (verbose) {
164             OUTMSG(("%o\n", access));
165         }
166         if (isPrivate) {
167             if (access != m_PrivateAccess) {
168                 uint32_t access = 0777;
169                 if (verbose) {
170                     OUTMSG(("updating %S to %o... ", str, access));
171                 }
172                 rc = KDirectorySetAccess(m_Self, false,
173                     m_PrivateAccess, access, str->addr);
174                 if (rc == 0) {
175                     OUTMSG(("ok\n"));
176                     updated = true;
177                 }
178                 else {
179                     OUTMSG(("failed\n"));
180                 }
181             }
182         }
183         else {
184             if ((access & m_PrivateAccess) != m_PrivateAccess) {
185                 uint32_t access = 0700;
186                 if (verbose) {
187                     OUTMSG(("updating %S to %o... ", str, access));
188                 }
189                 rc = KDirectorySetAccess(m_Self, false,
190                     m_PrivateAccess, access, str->addr);
191                 if (rc == 0) {
192                     OUTMSG(("ok\n"));
193                     updated = true;
194                 }
195                 else {
196                     OUTMSG(("failed\n"));
197                 }
198             }
199         }
200     }
201     return rc;
202 }
203 
CreateNonExistingDir(bool verbose,uint32_t access,const char * path,va_list args) const204 rc_t CKDirectory::CreateNonExistingDir(bool verbose,
205     uint32_t access, const char *path, va_list args) const
206 {
207     char str[PATH_MAX] = "";
208     rc_t rc = string_vprintf(str, sizeof str, NULL, path, args);
209     if (rc != 0) {
210         OUTMSG(("error: cannot generate path string\n"));
211         return rc;
212     }
213 
214     return CreateNonExistingDir(str, access, verbose, true);
215 }
216 
CreateNonExistingDir(const string & path,uint32_t access,bool verbose,bool checkExistance) const217 rc_t CKDirectory::CreateNonExistingDir(const string &path,
218     uint32_t access, bool verbose, bool checkExistance) const
219 {
220     const char *str = path.c_str();
221 
222     if (checkExistance) {
223         if (verbose) {
224             OUTMSG(("checking whether %s exists... ", str));
225         }
226         if (Exists(str)) {
227             if (verbose) {
228                 OUTMSG(("found\n"));
229             }
230             return 0;
231         }
232     }
233 
234     if (verbose) {
235         OUTMSG(("creating... "));
236     }
237 
238     rc_t rc = KDirectoryCreateDir(m_Self, access,
239         (kcmCreate | kcmParents), str);
240     if (verbose) {
241         if (rc == 0) {
242             OUTMSG(("ok\n"));
243         }
244         else {
245             OUTMSG(("failed\n"));
246         }
247     }
248 
249     return rc;
250 }
251 
CreateNonExistingDir(const CString & path,uint32_t access,bool verbose) const252 rc_t CKDirectory::CreateNonExistingDir(const CString &path,
253     uint32_t access, bool verbose) const
254 {
255     const String *str = path.Get();
256     if (str == NULL) {
257         return 0;
258     }
259 
260     if (verbose) {
261         OUTMSG(("checking whether %S exists... ", str));
262     }
263 
264     if (Exists(str->addr)) {
265         if (verbose) {
266             OUTMSG(("found\n"));
267         }
268         return 0;
269     }
270 
271     if (verbose) {
272         OUTMSG(("creating... "));
273     }
274 
275     rc_t rc = KDirectoryCreateDir(m_Self, access,
276         (kcmCreate | kcmParents), str->addr);
277     if (verbose) {
278         if (rc == 0) {
279             OUTMSG(("ok\n"));
280         }
281         else {
282             OUTMSG(("failed\n"));
283         }
284     }
285 
286     return rc;
287 }
288 
CKConfig(bool verbose)289 CKConfig::CKConfig(bool verbose)
290     : m_Self(NULL), m_Updated(false)
291     , m_RepositoryRemoteAuxDisabled ("repository/remote/aux/NCBI/disabled")
292     , m_RepositoryRemoteMainDisabled("repository/remote/main/CGI/disabled")
293     , m_RepositoryUserRoot          ("repository/user/main/public/root")
294 {
295     if (verbose)
296         OUTMSG(("loading configuration... "));
297     rc_t rc = KConfigMakeLocal(&m_Self, NULL);
298     if (rc == 0) {
299         if (verbose)
300             OUTMSG(("ok\n"));
301     }
302     else {
303         if (verbose)
304             OUTMSG(("failed\n"));
305         throw rc;
306     }
307 }
308 
Commit(void)309 rc_t CKConfig::Commit(void)
310 {
311     if (!m_Updated) {
312         return 0;
313     }
314 
315     rc_t rc = KConfigCommit(m_Self);
316     if ( rc == 0 )
317     {
318         m_Updated = false;
319     }
320     return rc;
321 }
322 
CreateRemoteRepositories(bool fix)323 rc_t CKConfig::CreateRemoteRepositories(bool fix) {
324     bool updated = NodeExists("/repository_remote/CGI/resolver-cgi/trace");
325 
326     rc_t rc = 0;
327 
328     {
329         const string name("/repository/remote/main/CGI/resolver-cgi");
330         if (!updated || !NodeExists(name)) {
331             rc_t r2 = UpdateNode(name,
332                 "https://trace.ncbi.nlm.nih.gov/Traces/names/names.fcgi");
333             if (r2 != 0 && rc == 0)
334                 rc = r2;
335         }
336     }
337 
338     if (fix) {
339         const string name("/repository/remote/main/CGI/disabled");
340         if (NodeExists(name)) {
341             rc_t r2 = UpdateNode(name.c_str(), "false");
342             if (r2 != 0 && rc == 0) {
343                 rc = r2;
344             }
345         }
346     }
347 
348     {
349         const string name("/repository/remote/protected/CGI/resolver-cgi");
350         if (!updated || !NodeExists(name)) {
351             rc_t r2 = UpdateNode(name,
352                 "https://trace.ncbi.nlm.nih.gov/Traces/names/names.fcgi");
353             if (r2 != 0 && rc == 0)
354                 rc = r2;
355         }
356     }
357 
358     if (fix) {
359         const string name("/repository/remote/disabled");
360         if (NodeExists(name)) {
361             rc_t r2 = UpdateNode(name.c_str(), "false");
362             if (r2 != 0 && rc == 0) {
363                 rc = r2;
364             }
365         }
366     }
367 
368     {
369         const string name("/repository/remote/main/SDL.2/resolver-cgi");
370         if (!NodeExists(name)) {
371             rc_t r2 = UpdateNode(name, SDL_CGI);
372             if (r2 != 0 && rc == 0)
373                 rc = r2;
374         }
375     }
376 
377     {
378         const string name("/repository/remote/protected/SDL.2/resolver-cgi");
379         if (!NodeExists(name)) {
380             rc_t r2 = UpdateNode(name, SDL_CGI);
381             if (r2 != 0 && rc == 0)
382                 rc = r2;
383         }
384     }
385 
386     return rc;
387 }
388 
CreateUserRepository(string repoName,bool fix)389 rc_t CKConfig::CreateUserRepository(string repoName, bool fix) {
390     if (repoName.size() == 0)
391         repoName = "public";
392 
393     CString cRoot(ReadString("/repository/user/default-path"));
394 
395     string root;
396     if (cRoot.Empty())
397         root = "$(HOME)/ncbi";
398     else
399         root = cRoot.GetString();
400 
401     std::ostringstream s;
402     s << "/repository/user/" << (repoName == "public" ? "main" : "protected")
403         << "/" << repoName;
404     string repoNode(s.str());
405     string name(repoNode + "/root");
406 
407     rc_t rc = 0;
408     /* create all nodes but root;
409     bool toFix = true;
410     if (fix)
411         toFix = !NodeExists(name);
412     if (toFix)
413         rc = UpdateNode(name, (root + "/public").c_str());
414 
415     {
416         rc_t r2 = UpdateNode(repoNode + "/cache-enabled", "true");
417         if (r2 != 0 && rc == 0)
418             rc = r2;
419     } */
420 
421     {
422         string name ( repoNode + "/apps/file/volumes/flat" );
423         if ( ! NodeExists ( name ) ) {
424             rc_t r2 = UpdateNode ( name, "files" );
425             if ( r2 != 0 && rc == 0 )
426                 rc = r2;
427         }
428     }
429     {
430         string name ( repoNode + "/apps/sra/volumes/sraFlat" );
431         if ( ! NodeExists ( name ) ) {
432             rc_t r2 = UpdateNode ( name, "sra" );
433             if ( r2 != 0 && rc == 0 )
434                 rc = r2;
435         }
436     }
437 
438     if ( repoName != "public" )
439         return rc;
440 
441     {
442         string name(repoNode + "/apps/sraPileup/volumes/withExtFlat");
443         if (!NodeExists(name)) {
444             rc_t r2 = UpdateNode(name, "sra");
445             if (r2 != 0 && rc == 0)
446                 rc = r2;
447         }
448     }
449     {
450         string name(repoNode + "/apps/sraRealign/volumes/withExtFlat");
451         if (!NodeExists(name)) {
452             rc_t r2 = UpdateNode(name, "sra");
453             if (r2 != 0 && rc == 0)
454                 rc = r2;
455         }
456     }
457     {
458         string name ( repoNode + "/apps/nakmer/volumes/nakmerFlat" );
459         if ( ! NodeExists ( name ) ) {
460             rc_t r2 = UpdateNode ( name, "nannot" );
461             if ( r2 != 0 && rc == 0 )
462                 rc = r2;
463         }
464     }
465     {
466         string name ( repoNode + "/apps/nannot/volumes/nannotFlat" );
467         if ( ! NodeExists ( name ) ) {
468             rc_t r2 = UpdateNode ( name, "nannot" );
469             if ( r2 != 0 && rc == 0 )
470                 rc = r2;
471         }
472     }
473     {
474         string name ( repoNode + "/apps/refseq/volumes/refseq" );
475         if ( ! NodeExists ( name ) ) {
476             rc_t r2 = UpdateNode ( name, "refseq" );
477             if ( r2 != 0 && rc == 0 )
478                 rc = r2;
479         }
480     }
481     {
482         string name ( repoNode + "/apps/wgs/volumes/wgsFlat" );
483         if ( ! NodeExists ( name ) ) {
484             rc_t r2 = UpdateNode ( name, "wgs" );
485             if ( r2 != 0 && rc == 0 )
486                 rc = r2;
487         }
488     }
489 
490     return rc;
491 }
492 
DisableRemoteRepository(bool disable)493 rc_t CKConfig::DisableRemoteRepository(bool disable) {
494     const char *value = disable ? "true" : "false";
495     rc_t rc = UpdateNode(m_RepositoryRemoteMainDisabled, value);
496 
497     rc_t r2 = UpdateNode(m_RepositoryRemoteAuxDisabled, value);
498     if (r2 != 0 && rc == 0) {
499         rc = r2;
500     }
501 
502     return rc;
503 }
504 
IsRemoteRepositoryDisabled(void) const505 bool CKConfig::IsRemoteRepositoryDisabled(void) const {
506     const CString disabled(ReadString(m_RepositoryRemoteMainDisabled));
507 
508     if (disabled.Equals("true")) {
509         const CString disabled(ReadString(m_RepositoryRemoteAuxDisabled));
510         return disabled.Equals("true");
511     }
512 
513     return false;
514 }
515 
NodeExists(const string & path) const516 bool CKConfig::NodeExists(const string &path) const {
517     const KConfigNode *n = OpenNodeRead(path.c_str());
518     if (n == NULL) {
519         return false;
520     }
521     KConfigNodeRelease(n);
522     return true;
523 }
524 
OpenNodeRead(const char * path,...) const525 const KConfigNode* CKConfig::OpenNodeRead(const char *path, ...) const {
526     va_list args;
527     va_start(args, path);
528 
529     const KConfigNode *node = NULL;
530     rc_t rc = KConfigVOpenNodeRead(m_Self, &node, path, args);
531 
532     va_end(args);
533 
534     if (rc != 0) {
535         return NULL;
536     }
537 
538     return node;
539 }
540 
ReadString(const char * path) const541 const String* CKConfig::ReadString(const char *path) const {
542     String *result = NULL;
543     rc_t rc = KConfigReadString(m_Self, path, &result);
544 
545     if (rc != 0) {
546         return NULL;
547     }
548     return result;
549 }
550 
Reload(bool verbose)551 void CKConfig::Reload(bool verbose) {
552     if (verbose) {
553         OUTMSG(("reloading configuration... "));
554     }
555 
556     rc_t rc = KConfigRelease(m_Self);
557     m_Self = NULL;
558 
559     if (rc == 0) {
560         rc = KConfigMakeLocal(&m_Self, NULL);
561     }
562 
563     if (rc == 0) {
564         if (verbose) {
565             OUTMSG(("ok\n"));
566         }
567         m_Updated = false;
568     }
569     else {
570         if (verbose) {
571             OUTMSG(("failed\n"));
572         }
573         throw rc;
574     }
575 }
576 
UpdateNode(const char * path,const char * buffer,bool verbose,size_t size)577 rc_t CKConfig::UpdateNode(const char *path,
578     const char *buffer, bool verbose, size_t size)
579 {
580     if (DEBUG) {
581         OUTMSG(("CKConfig::UpdateNode(%s, %s, %d)\n", path, buffer, size));
582     }
583 
584     if (verbose) {
585         OUTMSG(("%s = ... ", path));
586     }
587 
588     if (size == (size_t)~0) {
589         size = string_size(buffer);
590     }
591 
592     KConfigNode *node = NULL;
593     rc_t rc = KConfigOpenNodeUpdate(m_Self, &node, path);
594 // TODO do not write empty node if node itself is empty
595     if (rc == 0) {
596         rc = KConfigNodeWrite(node, buffer, size);
597     }
598     if (rc == 0) {
599         m_Updated = true;
600     }
601     RELEASE(KConfigNode, node);
602 
603     if (rc == 0) {
604         if (verbose) {
605             OUTMSG(("\"%s\"\n", buffer));
606         }
607     }
608     else {
609         if (verbose) {
610             OUTMSG(("failed: %R\n", buffer, rc));
611         }
612         else {
613             OUTMSG(("%s = ... failed: %R\n", path, rc));
614         }
615     }
616 
617     return rc;
618 }
619 
UpdateUserRepositoryRootPath(const CString & path)620 rc_t CKConfig::UpdateUserRepositoryRootPath(const CString &path) {
621     const String *str = path.Get();
622 
623     if (str == NULL) {
624         return 0;
625     }
626     return UpdateUserRepositoryRootPath(str->addr, str->size);
627 }
628 
StringRelease(const String * self)629 rc_t StringRelease(const String *self) {
630     free((void*)self);
631     return 0;
632 }
633 
Test(void)634 rc_t CSplitter::Test(void) {
635     String s;
636     {   StringInit(&s, NULL, 0, 0);
637         CSplitter p(&s);
638         assert(!p.HasNext());
639     }
640     {   CONST_STRING(&s, "");
641         CSplitter p(&s);
642         assert(!p.HasNext());
643     }
644     {
645         CONST_STRING(&s, "a");
646         CSplitter p(&s);
647         assert(p.HasNext());
648         assert(p.Next() == "a");
649         assert(!p.HasNext());
650     }
651     {
652         CONST_STRING(&s, "a:");
653         CSplitter p(&s);
654         assert(p.HasNext());
655         assert(p.Next() == "a");
656         assert(!p.HasNext());
657     }
658     {
659         CONST_STRING(&s, "a::");
660         CSplitter p(&s);
661         assert(p.HasNext());
662         assert(p.Next() == "a");
663         assert(!p.HasNext());
664     }
665     {
666         CONST_STRING(&s, "::a::");
667         CSplitter p(&s);
668         assert(p.HasNext());
669         assert(p.Next() == "a");
670         assert(!p.HasNext());
671     }
672     {
673         CONST_STRING(&s, "aa:bbb");
674         CSplitter p(&s);
675         assert(p.HasNext());
676         assert(p.Next() == "aa");
677         assert(p.HasNext());
678         assert(p.Next() == "bbb");
679         assert(!p.HasNext());
680     }   return 0;
681 }
682 
CUserConfigData(const CUserRepositories & repos,const CString & dflt)683 CUserConfigData::CUserConfigData(const CUserRepositories &repos,
684         const CString &dflt)
685     : m_DefaultRoot(dflt.GetString())
686     , m_CurrentRoot(repos.GetMainPublicRoot())
687     , m_CacheEnabled(repos.IsMainPublicCacheEnabled())
688 {}
689 
CreateNonExistingPublicDir(bool verbose,const char * path,...) const690 rc_t CKDirectory::CreateNonExistingPublicDir(bool verbose,
691     const char *path, ...) const
692 {
693     va_list args;
694     va_start(args, path);
695 
696     rc_t rc = CreateNonExistingDir(verbose, m_PublicAccess, path, args);
697 
698     va_end(args);
699 
700     return rc;
701 }
702 
IsDirectory(const char * path,...) const703 bool CKDirectory::IsDirectory(const char *path, ...) const {
704     va_list args;
705     va_start(args, path);
706 
707     KPathType type = KDirectoryVPathType(m_Self, path, args);
708 
709     va_end(args);
710 
711     return type == kptDir;
712 }
713 
Exists(const CString & path) const714 bool CKDirectory::Exists(const CString &path) const {
715     const String *str = path.Get();
716 
717     if (str == NULL) {
718         return false;
719     }
720     return Exists(str->addr);
721 }
722 
CheckPublicAccess(const CString & path,bool verbose) const723 rc_t CKDirectory::CheckPublicAccess(const CString &path, bool verbose) const {
724     bool updated = false;
725     return CheckAccess(path, updated, false, verbose);
726 }
727 
Canonical(const char * path) const728 std::string CKDirectory::Canonical ( const char * path ) const {
729     char resolved [ PATH_MAX ] = "";
730     rc_t rc =
731         KDirectoryResolvePath ( m_Self, true, resolved, sizeof resolved, path );
732     if ( rc != 0 )
733         return "";
734     size_t size  = string_measure ( path, NULL );
735     if ( string_cmp
736         ( path, size, resolved, string_measure ( resolved, NULL ), size ) == 0 )
737         return "";
738     return resolved;
739 }
740 
741 
UpdateNode(bool verbose,const char * value,const char * name,...)742 rc_t CKConfig::UpdateNode(bool verbose,
743     const char *value, const char *name, ...)
744 {
745     va_list args;
746     va_start(args, name);
747 
748     char dst[4096] = "";
749     size_t num_writ = 0;
750     rc_t rc = string_vprintf(dst, sizeof dst, &num_writ, name, args);
751     if (rc == 0) {
752         rc = UpdateNode(dst, value, verbose);
753     }
754 
755     va_end(args);
756 
757     return rc;
758 }
759 
FixResolverCgiNodes(void)760 rc_t CKConfig::FixResolverCgiNodes ( void ) {
761     rc_t rc = KConfigFixMainResolverCgiNode ( m_Self );
762     rc_t r2 = KConfigFixProtectedResolverCgiNode  ( m_Self );
763     if ( rc == 0 && r2 == 0 ) {
764         m_Updated = true;
765     } else {
766         if ( rc == 0 ) {
767             rc = r2;
768         }
769     }
770     return rc;
771 }
772 
773 
CApp(const CKDirectory & dir,const CKConfigNode & rep,const string & root,const string & name)774 CApp::CApp(const CKDirectory &dir, const CKConfigNode &rep,
775         const string &root, const string &name)
776     : m_HasVolume(false)
777     , m_AppVolPath("apps/" + name + "/volumes")
778 {
779     const KConfigNode *vols = rep.OpenNodeRead(m_AppVolPath);
780     KNamelist *typeNames = NULL;
781     rc_t rc = KConfigNodeListChildren(vols, &typeNames);
782     if (rc != 0) {
783         return;
784     }
785     uint32_t count = 0;
786     rc = KNamelistCount(typeNames, &count);
787     if (rc == 0) {
788         for (uint32_t idx = 0; idx < count; ++idx) {
789             const char *typeName = NULL;
790             rc = KNamelistGet(typeNames, idx, &typeName);
791             if (rc != 0) {
792                 continue;
793             }
794             const KConfigNode *alg = NULL;
795             rc = KConfigNodeOpenNodeRead(vols, &alg, typeName);
796             if (rc != 0) {
797                 continue;
798             }
799             String *volList = NULL;
800             rc = KConfigNodeReadString(alg, &volList);
801             if (rc == 0) {
802                 if (volList != NULL && volList->addr != NULL) {
803                     m_Volumes[typeName]
804                         = CAppVolume(typeName, volList->addr);
805                 }
806                 CSplitter volArray(volList);
807                 while (volArray.HasNext()) {
808                     const string vol(volArray.Next());
809                     if (dir.IsDirectory("%s/%s", root.c_str(), vol.c_str()))
810                     {
811                         m_HasVolume = true;
812                         break;
813                     }
814                 }
815             }
816             RELEASE(String, volList);
817             RELEASE(KConfigNode, alg);
818         }
819     }
820     RELEASE(KNamelist, typeNames);
821     RELEASE(KConfigNode, vols);
822 }
823 
Update(CKConfig & kfg,string & node,bool verbose)824 rc_t CRepository::Update(CKConfig &kfg, string &node, bool verbose) {
825     char root[4096] = "";
826     rc_t rc = string_printf(root, sizeof root, NULL, "/repository/%s/%s/%s",
827         m_Category.c_str(), m_SubCategory.c_str(), m_Name.c_str());
828     if (rc != 0) {
829         OUTMSG(("ERROR\n"));
830         return rc;
831     }
832 
833     if (DEBUG) {
834         OUTMSG(("CRepository::Update: root = %s\n", root));
835     }
836 
837     node.assign(root);
838 
839     for (CApps::TCI it = m_Apps.begin(); it != m_Apps.end(); ++it) {
840         rc_t r2 = (*it).second->Update(kfg, root, verbose);
841         if (r2 != 0 && rc == 0) {
842             rc = r2;
843         }
844     }
845 
846     rc_t r2 = kfg.UpdateNode(verbose, m_Root.c_str(), "%s/root", root);
847     if (r2 != 0 && rc == 0) {
848         rc = r2;
849     }
850 
851     return rc;
852 }
853 
CRepository(const string & category,const string & type,const string & name,const string & root)854 CRepository::CRepository(const string &category, const string &type,
855         const string &name, const string &root)
856     : m_Disabled(false)
857     , m_Category(category)
858     , m_SubCategory(type)
859     , m_Name(name)
860     , m_Root(root)
861 {}
862 
CRepository(const CKDirectory & dir,const CKConfigNode & repo,const string & category,const string & subCategory,const string & name)863 CRepository::CRepository(const CKDirectory &dir, const CKConfigNode &repo,
864         const string &category, const string &subCategory,
865         const string &name)
866     : m_Disabled(false)
867     , m_Category(category)
868     , m_SubCategory(subCategory)
869     , m_Name(name)
870     , m_Root(repo.ReadString("root"))
871 {
872     m_Apps.Update(dir, repo, m_Root, "sra");
873     m_Apps.Update(dir, repo, m_Root, "refseq");
874     string disabled(repo.ReadString("disabled"));
875     if (disabled == "true") {
876         m_Disabled = true;
877     }
878 }
879 
Dump(void) const880 string CRepository::Dump(void) const {
881     char node[4096] = "";
882     rc_t rc = string_printf(node, sizeof node, NULL,
883         "/repository/%s/%s/%s", m_Category.c_str(),
884         m_SubCategory.c_str(), m_Name.c_str(), m_Root.c_str());
885     if (rc != 0) {
886         OUTMSG(("ERROR\n"));
887         return "";
888     }
889 
890     for (CApps::TCI it = m_Apps.begin(); it != m_Apps.end(); ++it) {
891         (*it).second->Dump(node);
892     }
893 
894     if (m_Root.size() > 0) {
895         OUTMSG(("%s/root = \"%s\"\n", node, m_Root.c_str()));
896     }
897 
898     return node;
899 }
900 
Is(const string & subCategory,const string & name) const901 bool CRepository::Is(const string &subCategory, const string &name)
902     const
903 {
904     if (name.size() <= 0) {
905         return m_SubCategory == subCategory;
906     }
907 
908     return m_SubCategory == subCategory && m_Name == name;
909 }
910 
GetRoot(const string & stack,char needle)911 string CRemoteRepository::GetRoot(const string &stack, char needle)
912 {
913     if (EndsWith(stack, needle)) {
914         return "";
915     }
916     else {
917         return stack;
918     }
919 }
920 
GetCgi(const string & stack,char needle)921 string CRemoteRepository::GetCgi(const string &stack, char needle)
922 {
923     if (EndsWith(stack, needle)) {
924         return stack;
925     }
926     else {
927         return "";
928     }
929 }
930 
EndsWith(const string & stack,char needle)931 bool CRemoteRepository::EndsWith(const string &stack, char needle) {
932     if (stack.size() <= 0) {
933         return false;
934     }
935     return stack[stack.size() - 1] == needle;
936 }
937 
Fix(CKConfig & kfg,bool disable,bool verbose)938 rc_t CRemoteRepository::Fix(CKConfig &kfg, bool disable, bool verbose) {
939     if (verbose) {
940         OUTMSG(("checking %s %s remote repository\n",
941             GetCategory().c_str(), GetSubCategory().c_str()));
942     }
943 
944     Disable(disable);
945 
946     if (Is("main")) {
947         m_ResolverCgi
948             = "https://trace.ncbi.nlm.nih.gov/Traces/names/names.fcgi";
949         ClearApps();
950     }
951     else {
952         m_ResolverCgi = "";
953         FixApps();
954         SetRoot("https://ftp-trace.ncbi.nlm.nih.gov/sra");
955     }
956 
957     return Update(kfg);
958 }
959 
Fix(CKConfig & kfg,const CUserConfigData * data,const string * root)960 rc_t CUserRepository::Fix(CKConfig &kfg,
961     const CUserConfigData *data, const string *root)
962 {
963     Disable(false);
964 
965     if (DEBUG) {
966         OUTMSG((__FILE__ " CUserRepository::Fix: data = %d\n", data));
967     }
968 
969     if (data != NULL) {
970         m_CacheEnabled = data->GetCacheEnabled();
971         const string root(data->GetCurrentRoot());
972         if (root.size() > 0) {
973             if (DEBUG) {
974                 OUTMSG((__FILE__ " CUserRepository::Fix: SetRoot to %s\n",
975                     root.c_str()));
976             }
977             SetRoot(root);
978         }
979     }
980     else {
981         m_CacheEnabled = true;
982         assert(root);
983         SetRoot(*root);
984     }
985 
986     FixApps();
987 
988     return Update(kfg, DEBUG);
989 }
990 
Load(const CKConfig & kfg,const CKDirectory & dir)991 rc_t CUserRepositories::Load(const CKConfig &kfg, const CKDirectory &dir) {
992     const string category("main");
993 
994     OUTMSG(("loading %s user repository... ", category.c_str()));
995     const KConfigNode *userNode = kfg.OpenNodeRead
996         ("/repository/user/%s", category.c_str());
997     if (userNode == NULL) {
998         OUTMSG(("not found\n"));
999         return 0;
1000     }
1001     const CKConfigNode user(userNode);
1002     KNamelist *userNames = NULL;
1003     rc_t rc  = KConfigNodeListChildren(userNode, &userNames);
1004     if (rc != 0) {
1005         OUTMSG(("failed\n"));
1006     }
1007     uint32_t count = 0;
1008     if (rc == 0) {
1009         rc = KNamelistCount(userNames, &count);
1010     }
1011     if (rc != 0) {
1012         OUTMSG(("failed\n"));
1013     }
1014     for (uint32_t idx = 0; idx < count && rc == 0; ++idx) {
1015         const char *userName = NULL;
1016         rc = KNamelistGet(userNames, idx, &userName);
1017         if (rc != 0) {
1018             rc = 0;
1019             continue;
1020         }
1021         OUTMSG(("%s ", userName));
1022         const KConfigNode *userRepo = user.OpenNodeRead(userName);
1023         if (userRepo == NULL) {
1024             continue;
1025         }
1026         CKConfigNode node(userRepo);
1027         push_back(new CUserRepository(dir, node, category, userName));
1028     }
1029     RELEASE(KNamelist, userNames);
1030 
1031     if (rc == 0) {
1032         OUTMSG(("ok\n"));
1033     }
1034     else {
1035         OUTMSG(("failed\n"));
1036     }
1037 
1038     return rc;
1039 }
1040 
FindMainPublic(void) const1041 const CUserRepository *CUserRepositories::FindMainPublic(void) const {
1042     for (TCI it = begin(); it != end(); ++it) {
1043         CUserRepository *r = *it;
1044         assert(r);
1045         if (DEBUG) {
1046             OUTMSG(("MainPublic not found\n"));
1047         }
1048         if (r->Is("main", "public")) {
1049             return r;
1050         }
1051     }
1052 
1053     return NULL;
1054 }
1055 
MkAppVolumes(const CKDirectory & dir,bool verbose) const1056 rc_t CUserRepositories::MkAppVolumes(const CKDirectory &dir, bool verbose)
1057     const
1058 {
1059     rc_t rc = 0;
1060 
1061     for (TCI it = begin(); it != end(); ++it) {
1062         CUserRepository *r = *it;
1063         assert(r);
1064         const string root(r->GetRoot());
1065         if (root.size() <= 0) {
1066             continue;
1067         }
1068 
1069         for (CApps::TCI it = r->AppsBegin(); it != r->AppsEnd(); ++it) {
1070             const CApp *a = (*it).second;
1071             assert(a);
1072             //a->Dump("a->Dump");
1073             for (CApp::TCAppVolumesCI it = a->VolumesBegin();
1074                 it != a->VolumesEnd(); ++it)
1075             {
1076                 const CAppVolume &v((*it).second);
1077                 const string path(v.GetPath());
1078                 if (path.size() <= 0) {
1079                     continue;
1080                 }
1081                 CSplitter s(path);
1082                 while (s.HasNext()) {
1083                     rc_t r2 = dir.CreateNonExistingPublicDir
1084                         (verbose, "%s/%s", root.c_str(), s.Next().c_str());
1085                     if (r2 != 0 && rc == 0) {
1086                         rc = r2;
1087                     }
1088                 }
1089             }
1090         }
1091     }
1092 
1093     return rc;
1094 }
1095 
Load(const CKConfig & kfg,const CKDirectory & dir)1096 rc_t CRemoteRepositories::Load(const CKConfig &kfg, const CKDirectory &dir) {
1097     rc_t rc = 0;
1098     for (int i = 0; i < 2; ++i) {
1099         string category;
1100         switch (i) {
1101             case 0:
1102                 category = "main";
1103                 break;
1104             case 1:
1105                 category = "aux";
1106                 break;
1107             default:
1108                 assert(0);
1109                 break;
1110         }
1111         OUTMSG(("loading %s remote repository... ", category.c_str()));
1112         const KConfigNode *remoteNode = kfg.OpenNodeRead
1113             ("/repository/remote/%s", category.c_str());
1114         if (remoteNode == NULL) {
1115             OUTMSG(("not found\n"));
1116             continue;
1117         }
1118         const CKConfigNode remote(remoteNode);
1119         KNamelist *remoteNames = NULL;
1120         rc  = KConfigNodeListChildren(remoteNode, &remoteNames);
1121         if (rc != 0) {
1122             OUTMSG(("failed\n"));
1123         }
1124         uint32_t count = 0;
1125         if (rc == 0) {
1126             rc = KNamelistCount(remoteNames, &count);
1127         }
1128         if (rc != 0) {
1129             OUTMSG(("failed\n"));
1130         }
1131         for (uint32_t idx = 0; idx < count && rc == 0; ++idx) {
1132             const char *remoteName = NULL;
1133             rc = KNamelistGet(remoteNames, idx, &remoteName);
1134             if (rc != 0) {
1135                 rc = 0;
1136                 continue;
1137             }
1138             OUTMSG(("%s ", remoteName));
1139             const KConfigNode *remoteRepo = remote.OpenNodeRead(remoteName);
1140             if (remoteRepo == NULL) {
1141                 continue;
1142             }
1143             CKConfigNode node(remoteRepo);
1144             push_back(new CRemoteRepository(dir,
1145                 node, category, remoteName));
1146         }
1147         RELEASE(KNamelist, remoteNames);
1148         if (rc == 0) {
1149             OUTMSG(("ok\n"));
1150         }
1151         else {
1152             OUTMSG(("failed\n"));
1153         }
1154     }
1155     return rc;
1156 }
1157 
Fix(CKConfig & kfg,bool disable,bool verbose)1158 void CRemoteRepositories::Fix(CKConfig &kfg, bool disable, bool verbose) {
1159     CRemoteRepository *main = NULL;
1160     CRemoteRepository *protectd = NULL;
1161 
1162     for (TCI it = begin(); it != end(); ++it) {
1163         CRemoteRepository *r = *it;
1164         assert(r);
1165         bool toDisable = disable;
1166         const string category(r->GetCategory());
1167         if (category == "aux") {
1168             continue;
1169         }
1170         else if (category == "main") {
1171             main = r;
1172         }
1173         else if (category == "protected") {
1174             protectd = r;
1175             toDisable = false;
1176         }
1177         r->Fix(kfg, toDisable, verbose);
1178     }
1179 
1180     const string cgi
1181         ("https://trace.ncbi.nlm.nih.gov/Traces/names/names.fcgi");
1182     if (main == NULL) {
1183         main = new CRemoteRepository("main", "CGI", cgi);
1184         main->Fix(kfg, disable);
1185         push_back(main);
1186     }
1187 
1188     if (protectd == NULL) {
1189         protectd = new CRemoteRepository("protected", "CGI", cgi);
1190         if (verbose) {
1191             OUTMSG(("creating %s %s remote repository\n",
1192                 protectd->GetSubCategory().c_str(),
1193                 protectd->GetName().c_str()));
1194         }
1195         protectd->Fix(kfg, false);
1196         push_back(protectd);
1197     }
1198 }
1199