| 1 | // |
| 2 | // File2Page.cpp |
| 3 | // |
| 4 | // An application that creates a Page Compiler source file from an |
| 5 | // ordinary file. |
| 6 | // |
| 7 | // Copyright (c) 2008, Applied Informatics Software Engineering GmbH. |
| 8 | // and Contributors. |
| 9 | // |
| 10 | // SPDX-License-Identifier: BSL-1.0 |
| 11 | // |
| 12 | |
| 13 | |
| 14 | #include "Poco/Util/Application.h" |
| 15 | #include "Poco/Util/Option.h" |
| 16 | #include "Poco/Util/OptionSet.h" |
| 17 | #include "Poco/Util/HelpFormatter.h" |
| 18 | #include "Poco/Util/AbstractConfiguration.h" |
| 19 | #include "Poco/AutoPtr.h" |
| 20 | #include "Poco/File.h" |
| 21 | #include "Poco/Path.h" |
| 22 | #include "Poco/FileStream.h" |
| 23 | #include "Poco/NumberFormatter.h" |
| 24 | #include "Poco/DateTime.h" |
| 25 | #include <iostream> |
| 26 | |
| 27 | |
| 28 | using Poco::Util::Application; |
| 29 | using Poco::Util::Option; |
| 30 | using Poco::Util::OptionSet; |
| 31 | using Poco::Util::HelpFormatter; |
| 32 | using Poco::Util::AbstractConfiguration; |
| 33 | using Poco::Util::OptionCallback; |
| 34 | using Poco::NumberFormatter; |
| 35 | |
| 36 | |
| 37 | class File2PageApp: public Application |
| 38 | { |
| 39 | public: |
| 40 | File2PageApp(): _helpRequested(false) |
| 41 | { |
| 42 | } |
| 43 | |
| 44 | protected: |
| 45 | void initialize(Application& self) |
| 46 | { |
| 47 | loadConfiguration(); // load default configuration files, if present |
| 48 | Application::initialize(self); |
| 49 | // add your own initialization code here |
| 50 | } |
| 51 | |
| 52 | void uninitialize() |
| 53 | { |
| 54 | // add your own uninitialization code here |
| 55 | Application::uninitialize(); |
| 56 | } |
| 57 | |
| 58 | void reinitialize(Application& self) |
| 59 | { |
| 60 | Application::reinitialize(self); |
| 61 | // add your own reinitialization code here |
| 62 | } |
| 63 | |
| 64 | void defineOptions(OptionSet& options) |
| 65 | { |
| 66 | Application::defineOptions(options); |
| 67 | |
| 68 | options.addOption( |
| 69 | Option("help" , "h" , "Display help information on command line arguments." ) |
| 70 | .required(false) |
| 71 | .repeatable(false) |
| 72 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleHelp))); |
| 73 | |
| 74 | options.addOption( |
| 75 | Option("contentType" , "t" , "Specify a content type." ) |
| 76 | .required(false) |
| 77 | .repeatable(false) |
| 78 | .argument("MIME-Type" ) |
| 79 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleContentType))); |
| 80 | |
| 81 | options.addOption( |
| 82 | Option("contentLanguage" , "l" , "Specify a content language." ) |
| 83 | .required(false) |
| 84 | .repeatable(false) |
| 85 | .argument("language" ) |
| 86 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleContentLang))); |
| 87 | |
| 88 | options.addOption( |
| 89 | Option("class" , "c" , "Specify the handler class name." ) |
| 90 | .required(false) |
| 91 | .repeatable(false) |
| 92 | .argument("class-name" ) |
| 93 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleClassName))); |
| 94 | |
| 95 | options.addOption( |
| 96 | Option("namespace" , "n" , "Specify the handler class namespace name." ) |
| 97 | .required(false) |
| 98 | .repeatable(false) |
| 99 | .argument("namespace-name" ) |
| 100 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleNamespace))); |
| 101 | |
| 102 | options.addOption( |
| 103 | Option("output" , "o" , "Specify the output file name." ) |
| 104 | .required(false) |
| 105 | .repeatable(false) |
| 106 | .argument("path" ) |
| 107 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handleOutput))); |
| 108 | |
| 109 | options.addOption( |
| 110 | Option("path" , "p" , "Specify the server path of the file." ) |
| 111 | .required(false) |
| 112 | .repeatable(false) |
| 113 | .argument("path" ) |
| 114 | .callback(OptionCallback<File2PageApp>(this, &File2PageApp::handlePath))); |
| 115 | } |
| 116 | |
| 117 | void handleHelp(const std::string& name, const std::string& value) |
| 118 | { |
| 119 | _helpRequested = true; |
| 120 | displayHelp(); |
| 121 | stopOptionsProcessing(); |
| 122 | } |
| 123 | |
| 124 | void handleContentType(const std::string& name, const std::string& value) |
| 125 | { |
| 126 | _contentType = value; |
| 127 | } |
| 128 | |
| 129 | void handleContentLang(const std::string& name, const std::string& value) |
| 130 | { |
| 131 | _contentLang = value; |
| 132 | } |
| 133 | |
| 134 | void handleClassName(const std::string& name, const std::string& value) |
| 135 | { |
| 136 | _clazz = value; |
| 137 | } |
| 138 | |
| 139 | void handleNamespace(const std::string& name, const std::string& value) |
| 140 | { |
| 141 | _namespace = value; |
| 142 | } |
| 143 | |
| 144 | void handleOutput(const std::string& name, const std::string& value) |
| 145 | { |
| 146 | _output = value; |
| 147 | } |
| 148 | |
| 149 | void handlePath(const std::string& name, const std::string& value) |
| 150 | { |
| 151 | _path = value; |
| 152 | } |
| 153 | |
| 154 | void displayHelp() |
| 155 | { |
| 156 | HelpFormatter helpFormatter(options()); |
| 157 | helpFormatter.setCommand(commandName()); |
| 158 | helpFormatter.setUsage("OPTIONS" ); |
| 159 | helpFormatter.setHeader("Create a PageCompiler source file from a binary file." ); |
| 160 | helpFormatter.setIndent(8); |
| 161 | helpFormatter.format(std::cout); |
| 162 | } |
| 163 | |
| 164 | void convert(const std::string& path) |
| 165 | { |
| 166 | Poco::Path p(path); |
| 167 | Poco::Path op(path); |
| 168 | if (_output.empty()) |
| 169 | { |
| 170 | op.setExtension("cpsp" ); |
| 171 | } |
| 172 | else |
| 173 | { |
| 174 | op = _output; |
| 175 | } |
| 176 | if (_contentType.empty()) |
| 177 | { |
| 178 | _contentType = extToContentType(p.getExtension()); |
| 179 | } |
| 180 | if (_clazz.empty()) |
| 181 | { |
| 182 | _clazz = p.getBaseName(); |
| 183 | } |
| 184 | Poco::FileInputStream istr(path); |
| 185 | Poco::FileOutputStream ostr(op.toString()); |
| 186 | ostr << "<%@ page\n" |
| 187 | << " contentType=\"" << _contentType << "\"\n" ; |
| 188 | if (!_contentLang.empty()) |
| 189 | { |
| 190 | ostr << " contentLanguage=\"" << _contentLang << "\"\n" ; |
| 191 | } |
| 192 | ostr << " form=\"false\"\n" |
| 193 | << " namespace=\"" << _namespace << "\"\n" |
| 194 | << " class=\"" << _clazz << "\"\n" ; |
| 195 | if (!_path.empty()) |
| 196 | { |
| 197 | ostr << " path=\"" << _path << "\"\n" ; |
| 198 | } |
| 199 | ostr << " precondition=\"checkModified(request)\"%><%@" |
| 200 | << " impl include=\"Poco/DateTime.h\"\n" |
| 201 | << " include=\"Poco/DateTimeParser.h\"\n" |
| 202 | << " include=\"Poco/DateTimeFormatter.h\"\n" |
| 203 | << " include=\"Poco/DateTimeFormat.h\"%><%!\n\n" ; |
| 204 | ostr << "// " << path << "\n" ; |
| 205 | ostr << "static const unsigned char data[] = {\n\t" ; |
| 206 | int ch = istr.get(); |
| 207 | int pos = 0; |
| 208 | while (ch != -1) |
| 209 | { |
| 210 | ostr << "0x" << NumberFormatter::formatHex(ch, 2) << ", " ; |
| 211 | if (pos++ == 16) |
| 212 | { |
| 213 | ostr << "\n\t" ; |
| 214 | pos = 0; |
| 215 | } |
| 216 | ch = istr.get(); |
| 217 | } |
| 218 | Poco::File f(path); |
| 219 | Poco::DateTime lm = f.getLastModified(); |
| 220 | ostr << "\n};\n\n\n" ; |
| 221 | ostr << "static bool checkModified(Poco::Net::HTTPServerRequest& request)\n" |
| 222 | << "{\n" |
| 223 | << "\tPoco::DateTime modified(" << lm.year() << ", " << lm.month() << ", " << lm.day() << ", " << lm.hour() << ", " << lm.minute() << ", " << lm.second() << ");\n" |
| 224 | << "\trequest.response().setChunkedTransferEncoding(false);\n" |
| 225 | << "\trequest.response().set(\"Last-Modified\", Poco::DateTimeFormatter::format(modified, Poco::DateTimeFormat::HTTP_FORMAT));\n" |
| 226 | << "\tif (request.has(\"If-Modified-Since\"))\n" |
| 227 | << "\t{\n" |
| 228 | << "\t\tPoco::DateTime modifiedSince;\n" |
| 229 | << "\t\tint tzd;\n" |
| 230 | << "\t\tPoco::DateTimeParser::parse(request.get(\"If-Modified-Since\"), modifiedSince, tzd);\n" |
| 231 | << "\t\tif (modified <= modifiedSince)\n" |
| 232 | << "\t\t{\n" |
| 233 | << "\t\t\trequest.response().setContentLength(0);\n" |
| 234 | << "\t\t\trequest.response().setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_MODIFIED);\n" |
| 235 | << "\t\t\trequest.response().send();\n" |
| 236 | << "\t\t\treturn false;\n" |
| 237 | << "\t\t}\n" |
| 238 | << "\t}\n" |
| 239 | << "\trequest.response().setContentLength(static_cast<int>(sizeof(data)));\n" |
| 240 | << "\treturn true;\n" |
| 241 | << "}\n" |
| 242 | << "%><%\n" |
| 243 | << "\tresponseStream.write(reinterpret_cast<const char*>(data), sizeof(data));\n" |
| 244 | << "%>" ; |
| 245 | } |
| 246 | |
| 247 | std::string extToContentType(const std::string& ext) |
| 248 | { |
| 249 | if (ext == "jpg" || ext == "jpeg" ) |
| 250 | return "image/jpeg" ; |
| 251 | else if (ext == "png" ) |
| 252 | return "image/png" ; |
| 253 | else if (ext == "gif" ) |
| 254 | return "image/gif" ; |
| 255 | else if (ext == "ico" ) |
| 256 | return "image/x-icon" ; |
| 257 | else if (ext == "htm" ) |
| 258 | return "text/html" ; |
| 259 | else if (ext == "html" ) |
| 260 | return "text/html" ; |
| 261 | else if (ext == "css" ) |
| 262 | return "text/css" ; |
| 263 | else if (ext == "js" ) |
| 264 | return "application/javascript" ; |
| 265 | else if (ext == "xml" ) |
| 266 | return "text/xml" ; |
| 267 | else |
| 268 | return "application/binary" ; |
| 269 | } |
| 270 | |
| 271 | int main(const std::vector<std::string>& args) |
| 272 | { |
| 273 | if (!_helpRequested) |
| 274 | { |
| 275 | for (std::vector<std::string>::const_iterator it = args.begin(); it != args.end(); ++it) |
| 276 | { |
| 277 | convert(*it); |
| 278 | } |
| 279 | } |
| 280 | return Application::EXIT_OK; |
| 281 | } |
| 282 | |
| 283 | private: |
| 284 | bool _helpRequested; |
| 285 | std::string _contentType; |
| 286 | std::string _contentLang; |
| 287 | std::string _clazz; |
| 288 | std::string _namespace; |
| 289 | std::string _output; |
| 290 | std::string _path; |
| 291 | }; |
| 292 | |
| 293 | |
| 294 | POCO_APP_MAIN(File2PageApp) |
| 295 | |