comparison Plugins/Samples/ServeFolders/Plugin.cpp @ 1323:5a92665dee23

Sample plugin: Serve folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 13 Feb 2015 14:28:16 +0100
parents f497a72d9f71
children 3d76e26b3865
comparison
equal deleted inserted replaced
1322:f497a72d9f71 1323:5a92665dee23
25 **/ 25 **/
26 26
27 27
28 #include <OrthancCPlugin.h> 28 #include <OrthancCPlugin.h>
29 29
30 #include <json/reader.h>
31 #include <json/value.h>
30 #include <string.h> 32 #include <string.h>
31 #include <stdio.h> 33 #include <stdio.h>
34 #include <fstream>
35 #include <algorithm>
36 #include <sys/stat.h>
32 37
33 static OrthancPluginContext* context_ = NULL; 38 static OrthancPluginContext* context_ = NULL;
34 39 static std::map<std::string, std::string> folders_;
35 40 static const char* INDEX_URI = "/app/plugin-serve-folders.html";
36 static int32_t Callback(OrthancPluginRestOutput* output, 41
37 const char* url, 42
38 const OrthancPluginHttpRequest* request) 43 static const char* GetMimeType(const std::string& path)
39 { 44 {
45 size_t dot = path.find_last_of('.');
46
47 std::string extension = (dot == std::string::npos) ? "" : path.substr(dot);
48 std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
49
50 if (extension == ".html")
51 {
52 return "text/html";
53 }
54 else if (extension == ".css")
55 {
56 return "text/css";
57 }
58 else if (extension == ".js")
59 {
60 return "application/javascript";
61 }
62 else if (extension == ".gif")
63 {
64 return "image/gif";
65 }
66 else if (extension == ".svg")
67 {
68 return "image/svg+xml";
69 }
70 else if (extension == ".json")
71 {
72 return "application/json";
73 }
74 else if (extension == ".xml")
75 {
76 return "application/xml";
77 }
78 else if (extension == ".png")
79 {
80 return "image/png";
81 }
82 else if (extension == ".jpg" || extension == ".jpeg")
83 {
84 return "image/jpeg";
85 }
86 else
87 {
88 std::string s = "Unknown MIME type for extension: " + extension;
89 OrthancPluginLogWarning(context_, s.c_str());
90 return "application/octet-stream";
91 }
92 }
93
94
95
96 static bool ReadFile(std::string& content,
97 const std::string& path)
98 {
99 struct stat s;
100 if (stat(path.c_str(), &s) != 0 ||
101 !(s.st_mode & S_IFREG))
102 {
103 // Either the path does not exist, or it is not a regular file
104 return false;
105 }
106
107 FILE* fp = fopen(path.c_str(), "rb");
108 if (fp == NULL)
109 {
110 return false;
111 }
112
113 long size;
114
115 if (fseek(fp, 0, SEEK_END) == -1 ||
116 (size = ftell(fp)) < 0)
117 {
118 fclose(fp);
119 return false;
120 }
121
122 content.resize(size);
123
124 if (fseek(fp, 0, SEEK_SET) == -1)
125 {
126 fclose(fp);
127 return false;
128 }
129
130 bool ok = true;
131
132 if (size > 0 &&
133 fread(&content[0], size, 1, fp) != 1)
134 {
135 ok = false;
136 }
137
138 fclose(fp);
139
140 return ok;
141 }
142
143
144 static bool ReadConfiguration(Json::Value& configuration,
145 OrthancPluginContext* context)
146 {
147 std::string path;
148
149 {
150 char* pathTmp = OrthancPluginGetConfigurationPath(context);
151 if (pathTmp == NULL)
152 {
153 OrthancPluginLogError(context, "No configuration file is provided");
154 return false;
155 }
156
157 path = std::string(pathTmp);
158
159 OrthancPluginFreeString(context, pathTmp);
160 }
161
162 std::ifstream f(path.c_str());
163
164 Json::Reader reader;
165 if (!reader.parse(f, configuration) ||
166 configuration.type() != Json::objectValue)
167 {
168 std::string s = "Unable to parse the configuration file: " + std::string(path);
169 OrthancPluginLogError(context, s.c_str());
170 return false;
171 }
172
173 return true;
174 }
175
176
177 static int32_t FolderCallback(OrthancPluginRestOutput* output,
178 const char* url,
179 const OrthancPluginHttpRequest* request)
180 {
181 if (request->method != OrthancPluginHttpMethod_Get)
182 {
183 OrthancPluginSendMethodNotAllowed(context_, output, "GET");
184 return 0;
185 }
186
187 const std::string uri = request->groups[0];
188 const std::string item = request->groups[1];
189
190 std::map<std::string, std::string>::const_iterator found = folders_.find(uri);
191 if (found == folders_.end())
192 {
193 std::string s = "Unknown URI in plugin server-folders: " + uri;
194 OrthancPluginLogError(context_, s.c_str());
195 OrthancPluginSendHttpStatusCode(context_, output, 404);
196 return 0;
197 }
198
199 std::string path = found->second + "/" + item;
200 const char* mime = GetMimeType(path);
201
202 std::string s;
203 if (ReadFile(s, path))
204 {
205 const char* resource = s.size() ? s.c_str() : NULL;
206 OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
207 }
208 else
209 {
210 std::string s = "Inexistent file in served folder: " + path;
211 OrthancPluginLogError(context_, s.c_str());
212 OrthancPluginSendHttpStatusCode(context_, output, 404);
213 }
214
215 return 0;
216 }
217
218
219 static int32_t IndexCallback(OrthancPluginRestOutput* output,
220 const char* url,
221 const OrthancPluginHttpRequest* request)
222 {
223 if (request->method != OrthancPluginHttpMethod_Get)
224 {
225 OrthancPluginSendMethodNotAllowed(context_, output, "GET");
226 return 0;
227 }
228
229 std::string s = "<html><body><h1>Additional folders served by Orthanc</h1><ul>\n";
230
231 for (std::map<std::string, std::string>::const_iterator
232 it = folders_.begin(); it != folders_.end(); it++)
233 {
234 s += "<li><a href=\"" + it->first + "/index.html\">" + it->first + "</li>\n";
235 }
236
237 s += "</ul></body></html>";
238
239 OrthancPluginAnswerBuffer(context_, output, s.c_str(), s.size(), "text/html");
240
241 return 0;
40 } 242 }
41 243
42 244
43 245
44 extern "C" 246 extern "C"
54 sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin", 256 sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
55 context_->orthancVersion, 257 context_->orthancVersion,
56 ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER, 258 ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
57 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER, 259 ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
58 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER); 260 ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
59 OrthancPluginLogError(context, info); 261 OrthancPluginLogError(context_, info);
60 return -1; 262 return -1;
61 } 263 }
62 264
63 OrthancPluginSetDescription(context_, "Serve additional folders with the HTTP server of Orthanc."); 265 OrthancPluginSetDescription(context_, "Serve additional folders with the HTTP server of Orthanc.");
64 266
267 Json::Value configuration;
268 if (!ReadConfiguration(configuration, context_))
269 {
270 return -1;
271 }
272
273 if (configuration.isMember("ServeFolders") &&
274 configuration["ServeFolders"].type() == Json::objectValue)
275 {
276 Json::Value::Members members = configuration["ServeFolders"].getMemberNames();
277
278 // Register the callback for each base URI
279 for (Json::Value::Members::const_iterator
280 it = members.begin(); it != members.end(); it++)
281 {
282 const std::string& baseUri = *it;
283 const std::string path = configuration["ServeFolders"][*it].asString();
284 const std::string regex = "(" + baseUri + ")/(.*)";
285
286 if (baseUri.empty() ||
287 *baseUri.rbegin() == '/')
288 {
289 std::string message = "The URI of a folder to be server cannot be empty or end with a '/': " + *it;
290 OrthancPluginLogWarning(context_, message.c_str());
291 return -1;
292 }
293
294 OrthancPluginRegisterRestCallback(context, regex.c_str(), FolderCallback);
295 folders_[baseUri] = path;
296 }
297
298 OrthancPluginRegisterRestCallback(context, INDEX_URI, IndexCallback);
299 OrthancPluginSetRootUri(context, INDEX_URI);
300 }
301 else
302 {
303 OrthancPluginLogWarning(context_, "No section \"ServeFolders\" in your configuration file: "
304 "No additional folder will be served!");
305 }
306
65 return 0; 307 return 0;
66 } 308 }
67 309
68 310
69 ORTHANC_PLUGINS_API void OrthancPluginFinalize() 311 ORTHANC_PLUGINS_API void OrthancPluginFinalize()