ff3e6254bff036f88a460c15522f0678f47d15c6
[anna.git] / example / diameter / batchConverter / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // http://redmine.teslayout.com/projects/anna-suite
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     *  Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36 // Standard
37 #include <iostream>
38 #include <fstream>
39
40 // STL
41 #include <string>
42 #include <map>
43
44 #include <anna/core/DataBlock.hpp>
45 #include <anna/core/util/Tokenizer.hpp>
46 #include <anna/core/functions.hpp>
47 #include <anna/core/tracing/Logger.hpp>
48 #include <anna/core/tracing/TraceWriter.hpp>
49 #include <anna/core/RuntimeException.hpp>
50 #include <anna/io/Directory.hpp>
51 #include <anna/xml/xml.hpp>
52 #include <anna/diameter/stack/Engine.hpp>
53 #include <anna/diameter/codec/Engine.hpp>
54 #include <anna/diameter/codec/Message.hpp>
55 //#include <anna/diameter/codec/functions.hpp> // ApplicationId anna::diameter::codec::functions::getApplicationId(const anna::DataBlock &) throw(anna::RuntimeException);
56
57
58 using namespace anna;
59 using namespace anna::diameter;
60
61 anna::diameter::codec::Message *G_codecMsg;
62 anna::diameter::codec::Engine *G_codecEngine;
63
64 bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
65   // Get hex string
66   static char buffer[8192];
67   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
68
69   if(infile.is_open()) {
70     infile >> buffer;
71     std::string hexString(buffer, strlen(buffer));
72     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
73     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
74     LOGDEBUG(
75       std::string msg = "Hex string (remove colons if exists): ";
76       msg += hexString;
77       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
78     );
79     anna::functions::fromHexString(hexString, db);
80     // Close file
81     infile.close();
82     return true;
83   }
84
85   return false;
86 }
87
88
89 void _exit(const std::string &message, int resultCode = 1) {
90   if(resultCode)
91     std::cerr << message << std::endl << std::endl;
92   else
93     std::cout << message << std::endl << std::endl;
94
95   exit(resultCode);
96 }
97
98 // Decodes a diameter message coming from a datablock
99 void decodeDataBlock(const anna::DataBlock &db/*, unsigned int & detectedApplicationId*/) throw() {
100   try {
101     //detectedApplicationId = anna::diameter::codec::functions::getApplicationId(db);
102     //G_codecEngine->setDictionary(detectedApplicationId); we enabled this feature in the codec engine: selectStackWithApplicationId(true);
103     G_codecMsg->decode(db);
104   } catch(RuntimeException &ex) {
105     _exit(ex.asString());
106   }
107 }
108
109 //-------------------------------------------------------------------
110 int main(int argc, char **argv) {
111   std::string exec = argv[0];
112   std::string execBN = exec.substr(exec.find_last_of("/") + 1);
113   std::string filetrace = execBN + ".trace";
114   std::cout << std::endl;
115
116   //check command line arguments
117   if(argc < 3) {
118     std::string msg = "Usage: "; msg += exec;
119     msg += " <stacks> <working directory> [--hex-only] [--xml-only] [--no-validation] [--ignore-flags] [--debug]\n\n";
120     msg += "       stacks:                  <id1,dictionary1#id2,dictionary2#...#idN,dictionaryN>\n";
121     msg += "                                This is a list of #-separated stacks defined by a comma-separated pair <application-id,xml dictionary pathfile>\n";
122     msg += "                                If only one stack is provided, application-id could be omitted and then, all the messages will be decoded with the\n";
123     msg += "                                dictionary regardless the value of the application-id (the stack will be registered with id=0).\n";
124     msg += "       Working directory:       Contains 'xml files' (ANNA-Diameter message format) and/or 'hex files' (hexadecimal content, colons allowed).\n";
125     msg += "       --hex-only:              Only hex files are converted.\n";
126     msg += "       --xml-only:              Only xml files are converted.\n";
127     msg += "       --no-validation:         no validation is performed.\n";
128     msg += "       --ignore-flags:          wrong flags regarding dictionary are ignored in xml representation.\n";
129     msg += "       --debug:                 activates debug level traces (warning by default).\n";
130     msg += "\n";
131     msg += "       The batch process will translate '.xml' files into '.xml.as.hex', and '.hex' files into '.hex.as.xml'\n";
132     msg += "       Take care not to include the dictionary files (which have '.xml' extension) in the working directory, or there will be an exception.\n";
133     _exit(msg);
134   }
135
136   // Command-line parameters:
137   std::string stacks = argv[1];
138   std::string wkDir = argv[2];
139   std::string optionals;
140   int indx = 3;
141   while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
142
143   bool no_validation = (optionals.find("--no-validation") != std::string::npos);
144   bool ignore_flags = (optionals.find("--ignore-flags") != std::string::npos);
145   bool debug = (optionals.find("--debug") != std::string::npos);
146   bool hexOnly = (optionals.find("--hex-only") != std::string::npos);
147   bool xmlOnly = (optionals.find("--xml-only") != std::string::npos);
148   if (hexOnly && xmlOnly) _exit("Cannot provide both '--hex-only' and '--xml-only' !!");
149   bool processXml = hexOnly ? false:true;
150   bool processHex = xmlOnly ? false:true;
151   Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
152   Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
153   G_codecEngine = new anna::diameter::codec::Engine();
154   G_codecMsg = G_codecEngine->createMessage();
155   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
156
157   // Register stacks:
158   bool multistack = false;
159   try {
160     anna::Tokenizer stacksTok;
161     stacksTok.apply(stacks, "#");
162     anna::Tokenizer::const_iterator stacks_it, stack_it;
163
164     for(stacks_it = stacksTok.begin(); stacks_it != stacksTok.end(); stacks_it++) {
165       std::string stack = anna::Tokenizer::data(stacks_it);
166       anna::Tokenizer stackTok;
167       stackTok.apply(stack, ",");
168
169       if(stackTok.size() == 1) {
170         if(stacksTok.size() != 1)
171           throw anna::RuntimeException("Application Id value is mandatory when more than one stack is going to be configured", ANNA_FILE_LOCATION);
172
173         anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* no matter */, stack); // the stack is the dictionary
174         G_codecEngine->setDictionary(d);
175         break;
176       }
177
178       if(stackTok.size() != 2)
179         throw anna::RuntimeException("Each stack must be in the form '<application-id>#<xml dictionary pathfile>'", ANNA_FILE_LOCATION);
180
181       multistack = true;
182       stack_it = stackTok.begin();
183       unsigned int stackId = atoll(anna::Tokenizer::data(stack_it));
184       stack_it++;
185       std::string file = anna::Tokenizer::data(stack_it);
186       anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(stackId, file);
187     }
188
189     // Auto stack selection based on Application-ID:
190     if (multistack) G_codecEngine->selectStackWithApplicationId(true);
191
192     std::cout << "Stacks provided:          " << std::endl;
193     std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
194     std::cout << std::endl;
195     std::cout << "Working directory:        " << wkDir << std::endl;
196     std::cout << "Validation:               " << (!no_validation ? "yes" : "no") << std::endl;
197     std::cout << "Ignore Flags:             " << (ignore_flags ? "yes" : "no") << std::endl;
198     std::cout << std::endl;
199   } catch(anna::RuntimeException &ex) {
200     _exit(ex.asString());
201   }
202
203   // Validation kindness
204   G_codecEngine->setFixMode(anna::diameter::codec::EngineImpl::FixMode::Never); // we will encode "as is" (because --no-validation is assumed as user desire)
205   G_codecEngine->setValidationDepth(anna::diameter::codec::EngineImpl::ValidationDepth::Complete); // complete validation for better reports
206   if(no_validation) G_codecEngine->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
207   if(ignore_flags) G_codecEngine->ignoreFlagsOnValidation(true);
208
209   // Auxiliary variables:
210   anna::DataBlock db_aux(true);
211   anna::io::Directory directoryHex, directoryXml; // we separate (although it wouldn't be neccessary using setPattern each time), because we don't
212                                                   // want to process the resulting xml files from hex conversion, only the xml files present at the
213                                                   // beginning in the directory
214   // Analyze FS:
215   directoryHex.setPattern(".hex$");
216   directoryHex.read(wkDir.c_str(), anna::io::Directory::Mode::FullPath);
217   directoryXml.setPattern(".xml$");
218   directoryXml.read(wkDir.c_str(), anna::io::Directory::Mode::FullPath);
219   
220   // Processing .hex files:
221   bool anyHexConverted = false;
222   if (processHex) {
223     for (anna::io::Directory::const_iterator it = directoryHex.begin(); it != directoryHex.end(); it++) {
224       const std::string& entry = anna::io::Directory::data (it);
225       LOGDEBUG(anna::Logger::debug(entry + " is being converted to xml", ANNA_FILE_LOCATION));
226
227       if(!getDataBlockFromHexFile(entry, db_aux))
228         _exit("Error reading hex file provided");
229
230       // Decode datablock:
231       decodeDataBlock(db_aux);
232
233       // Write conversion:
234       std::string outputFile = entry + ".as.xml";
235       std::ofstream out(outputFile.c_str(), std::ifstream::out);
236       out << G_codecMsg->asXMLString();
237       out.close();
238
239       anyHexConverted = true;
240     }
241   }
242
243   // Processing .xml files:
244   bool anyXmlConverted = false;
245   if (processXml) {
246     for (anna::io::Directory::const_iterator it = directoryXml.begin(); it != directoryXml.end(); it++) {
247       const std::string& entry = anna::io::Directory::data (it);
248       LOGDEBUG(anna::Logger::debug(entry + " is being converted to hex", ANNA_FILE_LOCATION));
249   
250       // Load file:
251       G_codecMsg->loadXML(entry);  
252       
253       // Write conversion:
254       std::string hexString = anna::functions::asHexString(G_codecMsg->code());
255       std::string outputFile = entry + ".as.hex";
256       std::ofstream out(outputFile.c_str(), std::ifstream::out);
257       out.write(hexString.c_str(), hexString.size());
258       out.close();
259   
260       anyXmlConverted = true;
261     }
262   }
263
264
265   std::string msg = "Open '"; msg += filetrace; msg += "' in order to see process traces.\n";
266   if (anyHexConverted) msg += "Open '*.hex.as.xml' files to see decoding results.\n";
267   if (anyXmlConverted) msg += "Open '*.xml.as.hex' files to see encoding results.\n";
268   _exit(msg, 0);
269 }
270