1 /*
2  * Copyright (C) 2020 Luciano Iam <oss@lucianoiam.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 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 General Public License for more details.
13  *
14  * You should have received a copy of the/GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include <iostream>
20 #include <sstream>
21 
22 #include <glibmm/fileutils.h>
23 #include <glibmm/miscutils.h>
24 
25 #include "ardour/filesystem_paths.h"
26 #include "pbd/file_utils.h"
27 
28 #include "resources.h"
29 #include "json.h"
30 
31 using namespace ArdourSurface;
32 
33 static const char* const data_dir_env_var = "ARDOUR_WEBSURFACES_PATH";
34 static const char* const data_dir_name = "web_surfaces";
35 static const char* const builtin_dir_name = "builtin";
36 static const char* const user_dir_name = "user";
37 
38 static bool
dir_filter(const std::string & str,void *)39 dir_filter (const std::string &str, void* /*arg*/)
40 {
41 	return Glib::file_test (str, Glib::FILE_TEST_IS_DIR);
42 }
43 
ServerResources()44 ServerResources::ServerResources ()
45     : _index_dir ("")
46     , _builtin_dir ("")
47     , _user_dir ("")
48 {
49 }
50 
51 const std::string&
index_dir()52 ServerResources::index_dir ()
53 {
54 	if (_index_dir.empty ()) {
55 		_index_dir = server_data_dir ();
56 	}
57 
58 	return _index_dir;
59 }
60 
61 const std::string&
builtin_dir()62 ServerResources::builtin_dir ()
63 {
64 	if (_builtin_dir.empty ()) {
65 		_builtin_dir = Glib::build_filename (server_data_dir (), builtin_dir_name);
66 	}
67 
68 	return _builtin_dir;
69 }
70 
71 const std::string&
user_dir()72 ServerResources::user_dir ()
73 {
74 	if (_user_dir.empty ()) {
75 		_user_dir = Glib::build_filename (ARDOUR::user_config_directory (), data_dir_name);
76 	}
77 
78 	return _user_dir;
79 }
80 
81 std::string
scan()82 ServerResources::scan ()
83 {
84 	std::stringstream ss;
85 
86 	std::string builtin_dir_str   = PBD::canonical_path (builtin_dir ());
87 	SurfaceManifestVector builtin = read_manifests (builtin_dir_str);
88 
89 	ss << "[{"
90 		<< "\"filesystemPath\":\"" << WebSocketsJSON::escape (builtin_dir_str) << "\""
91 		<< ",\"path\":\"" << WebSocketsJSON::escape (builtin_dir_name) << "\""
92 		<< ",\"surfaces\":"
93 		<< "[";
94 
95 	for (SurfaceManifestVector::iterator it = builtin.begin (); it != builtin.end (); ) {
96 		ss << it->to_json ();
97 		if (++it != builtin.end()) {
98 			ss << ",";
99 		}
100 	}
101 
102 	std::string user_dir_str   = PBD::canonical_path (user_dir ());
103 	SurfaceManifestVector user = read_manifests (user_dir_str);
104 
105 	ss << "]},{"
106 		<< "\"filesystemPath\":\"" << WebSocketsJSON::escape (user_dir_str) << "\""
107 		<< ",\"path\":\"" << WebSocketsJSON::escape (user_dir_name) << "\""
108 		<< ",\"surfaces\":"
109 		<< "[";
110 
111 	for (SurfaceManifestVector::iterator it = user.begin (); it != user.end (); ) {
112 		ss << it->to_json ();
113 		if (++it != user.end()) {
114 			ss << ",";
115 		}
116 	}
117 
118 	ss << "]}]";
119 
120 	return ss.str ();
121 }
122 
123 std::string
server_data_dir()124 ServerResources::server_data_dir ()
125 {
126 	std::string data_dir;
127 
128 	bool defined = false;
129 	std::string env_dir (Glib::getenv (data_dir_env_var, defined));
130 
131 	if (defined && !env_dir.empty ()) {
132 		/* useful for development */
133 		data_dir = env_dir;
134 	} else {
135 		/* use reverse iterator, since ardour_data_search_path() prefixes the user-data dir */
136 		PBD::Searchpath s (ARDOUR::ardour_data_search_path ());
137 		for (PBD::Searchpath::reverse_iterator i = s.rbegin (); i != s.rend(); ++i) {
138 			data_dir = Glib::build_filename (*i, data_dir_name);
139 			if (Glib::file_test(data_dir, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_DIR)) {
140 				break;
141 			}
142 		}
143 	}
144 
145 	return data_dir;
146 }
147 
148 SurfaceManifestVector
read_manifests(std::string dir)149 ServerResources::read_manifests (std::string dir)
150 {
151 	SurfaceManifestVector    result;
152 	std::vector<std::string> subdirs;
153 	PBD::Searchpath          spath (dir);
154 
155 	find_paths_matching_filter (subdirs, spath, dir_filter,
156 		0 /*arg*/, true /*pass_fullpath*/, true /*return_fullpath*/, false /*recurse*/);
157 
158 	for (std::vector<std::string>::const_iterator it = subdirs.begin (); it != subdirs.end (); ++it) {
159 		if (!SurfaceManifest::exists_at_path (*it)) {
160 			continue;
161 		}
162 
163 		SurfaceManifest manifest (*it);
164 
165 		if (manifest.valid ()) {
166 			result.push_back (manifest);
167 		}
168 	}
169 
170 	return result;
171 }
172