Changed LICENSE. Now referenced to web site and file on project root directory
[anna.git] / example / diameter / batchConverter / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <iostream>
10 #include <fstream>
11
12 // STL
13 #include <string>
14 #include <map>
15
16 #include <anna/core/DataBlock.hpp>
17 #include <anna/core/util/Tokenizer.hpp>
18 #include <anna/core/functions.hpp>
19 #include <anna/core/tracing/Logger.hpp>
20 #include <anna/core/tracing/TraceWriter.hpp>
21 #include <anna/core/RuntimeException.hpp>
22 #include <anna/io/Directory.hpp>
23 #include <anna/xml/xml.hpp>
24 #include <anna/diameter/stack/Engine.hpp>
25 #include <anna/diameter/codec/Engine.hpp>
26 #include <anna/diameter/codec/Message.hpp>
27 //#include <anna/diameter/codec/functions.hpp> // ApplicationId anna::diameter::codec::functions::getApplicationId(const anna::DataBlock &) throw(anna::RuntimeException);
28
29
30 using namespace anna;
31 using namespace anna::diameter;
32
33 anna::diameter::codec::Message *G_codecMsg;
34 anna::diameter::codec::Engine *G_codecEngine;
35
36 bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) throw() {
37   // Get hex string
38   static char buffer[8192];
39   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
40
41   if(infile.is_open()) {
42     infile >> buffer;
43     std::string hexString(buffer, strlen(buffer));
44     // Allow colon separator in hex string: we have to remove them before processing with 'fromHexString':
45     hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
46     LOGDEBUG(
47       std::string msg = "Hex string (remove colons if exists): ";
48       msg += hexString;
49       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
50     );
51     anna::functions::fromHexString(hexString, db);
52     // Close file
53     infile.close();
54     return true;
55   }
56
57   return false;
58 }
59
60
61 void _exit(const std::string &message, int resultCode = 1) {
62   if(resultCode)
63     std::cerr << message << std::endl << std::endl;
64   else
65     std::cout << message << std::endl << std::endl;
66
67   exit(resultCode);
68 }
69
70 // Decodes a diameter message coming from a datablock
71 void decodeDataBlock(const anna::DataBlock &db/*, unsigned int & detectedApplicationId*/) throw() {
72   try {
73     //detectedApplicationId = anna::diameter::codec::functions::getApplicationId(db);
74     //G_codecEngine->setDictionary(detectedApplicationId); we enabled this feature in the codec engine: selectStackWithApplicationId(true);
75     G_codecMsg->decode(db);
76   } catch(RuntimeException &ex) {
77     _exit(ex.asString());
78   }
79 }
80
81 //-------------------------------------------------------------------
82 int main(int argc, char **argv) {
83   std::string exec = argv[0];
84   std::string execBN = exec.substr(exec.find_last_of("/") + 1);
85   std::string filetrace = execBN + ".trace";
86   std::cout << std::endl;
87
88   //check command line arguments
89   if(argc < 3) {
90     std::string msg = "Usage: "; msg += exec;
91     msg += " <stacks> <working directory> [--hex-only] [--xml-only] [--no-validation] [--ignore-flags] [--debug]\n\n";
92     msg += "       stacks:                  <id1,dictionary1#id2,dictionary2#...#idN,dictionaryN>\n";
93     msg += "                                This is a list of #-separated stacks defined by a comma-separated pair <application-id,xml dictionary pathfile>\n";
94     msg += "                                If only one stack is provided, application-id could be omitted and then, all the messages will be decoded with the\n";
95     msg += "                                dictionary regardless the value of the application-id (the stack will be registered with id=0).\n";
96     msg += "       Working directory:       Contains 'xml files' (ANNA-Diameter message format) and/or 'hex files' (hexadecimal content, colons allowed).\n";
97     msg += "       --hex-only:              Only hex files are converted.\n";
98     msg += "       --xml-only:              Only xml files are converted.\n";
99     msg += "       --no-validation:         no validation is performed.\n";
100     msg += "       --ignore-flags:          wrong flags regarding dictionary are ignored in xml representation.\n";
101     msg += "       --debug:                 activates debug level traces (warning by default).\n";
102     msg += "\n";
103     msg += "       The batch process will translate '.xml' files into '.xml.as.hex', and '.hex' files into '.hex.as.xml'\n";
104     msg += "       Take care not to include the dictionary files (which have '.xml' extension) in the working directory, or there will be an exception.\n";
105     _exit(msg);
106   }
107
108   // Command-line parameters:
109   std::string stacks = argv[1];
110   std::string wkDir = argv[2];
111   std::string optionals;
112   int indx = 3;
113   while(indx < argc) { optionals += " "; optionals += argv[indx]; indx++; }
114
115   bool no_validation = (optionals.find("--no-validation") != std::string::npos);
116   bool ignore_flags = (optionals.find("--ignore-flags") != std::string::npos);
117   bool debug = (optionals.find("--debug") != std::string::npos);
118   bool hexOnly = (optionals.find("--hex-only") != std::string::npos);
119   bool xmlOnly = (optionals.find("--xml-only") != std::string::npos);
120   if (hexOnly && xmlOnly) _exit("Cannot provide both '--hex-only' and '--xml-only' !!");
121   bool processXml = hexOnly ? false:true;
122   bool processHex = xmlOnly ? false:true;
123   Logger::setLevel(debug ? Logger::Debug:Logger::Warning);
124   Logger::initialize(execBN.c_str(), new TraceWriter(filetrace.c_str(), 2048000));
125   G_codecEngine = new anna::diameter::codec::Engine();
126   G_codecMsg = G_codecEngine->createMessage();
127   anna::diameter::stack::Engine &stackEngine = anna::diameter::stack::Engine::instantiate();
128
129   // Register stacks:
130   bool multistack = false;
131   try {
132     anna::Tokenizer stacksTok;
133     stacksTok.apply(stacks, "#");
134     anna::Tokenizer::const_iterator stacks_it, stack_it;
135
136     for(stacks_it = stacksTok.begin(); stacks_it != stacksTok.end(); stacks_it++) {
137       std::string stack = anna::Tokenizer::data(stacks_it);
138       anna::Tokenizer stackTok;
139       stackTok.apply(stack, ",");
140
141       if(stackTok.size() == 1) {
142         if(stacksTok.size() != 1)
143           throw anna::RuntimeException("Application Id value is mandatory when more than one stack is going to be configured", ANNA_FILE_LOCATION);
144
145         anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(0 /* no matter */, stack); // the stack is the dictionary
146         G_codecEngine->setDictionary(d);
147         break;
148       }
149
150       if(stackTok.size() != 2)
151         throw anna::RuntimeException("Each stack must be in the form '<application-id>#<xml dictionary pathfile>'", ANNA_FILE_LOCATION);
152
153       multistack = true;
154       stack_it = stackTok.begin();
155       unsigned int stackId = atoll(anna::Tokenizer::data(stack_it));
156       stack_it++;
157       std::string file = anna::Tokenizer::data(stack_it);
158       anna::diameter::stack::Dictionary * d = stackEngine.createDictionary(stackId, file);
159     }
160
161     // Auto stack selection based on Application-ID:
162     if (multistack) G_codecEngine->selectStackWithApplicationId(true);
163
164     std::cout << "Stacks provided:          " << std::endl;
165     std::cout << anna::functions::tab(stackEngine.asString(false /* light */));
166     std::cout << std::endl;
167     std::cout << "Working directory:        " << wkDir << std::endl;
168     std::cout << "Validation:               " << (!no_validation ? "yes" : "no") << std::endl;
169     std::cout << "Ignore Flags:             " << (ignore_flags ? "yes" : "no") << std::endl;
170     std::cout << std::endl;
171   } catch(anna::RuntimeException &ex) {
172     _exit(ex.asString());
173   }
174
175   // Validation kindness
176   G_codecEngine->setFixMode(anna::diameter::codec::EngineImpl::FixMode::Never); // we will encode "as is" (because --no-validation is assumed as user desire)
177   G_codecEngine->setValidationDepth(anna::diameter::codec::EngineImpl::ValidationDepth::Complete); // complete validation for better reports
178   if(no_validation) G_codecEngine->setValidationMode(anna::diameter::codec::EngineImpl::ValidationMode::Never);
179   if(ignore_flags) G_codecEngine->ignoreFlagsOnValidation(true);
180
181   // Auxiliary variables:
182   anna::DataBlock db_aux(true);
183   anna::io::Directory directoryHex, directoryXml; // we separate (although it wouldn't be neccessary using setPattern each time), because we don't
184                                                   // want to process the resulting xml files from hex conversion, only the xml files present at the
185                                                   // beginning in the directory
186   // Analyze FS:
187   directoryHex.setPattern(".hex$");
188   directoryHex.read(wkDir.c_str(), anna::io::Directory::Mode::FullPath);
189   directoryXml.setPattern(".xml$");
190   directoryXml.read(wkDir.c_str(), anna::io::Directory::Mode::FullPath);
191   
192   // Processing .hex files:
193   bool anyHexConverted = false;
194   if (processHex) {
195     for (anna::io::Directory::const_iterator it = directoryHex.begin(); it != directoryHex.end(); it++) {
196       const std::string& entry = anna::io::Directory::data (it);
197       LOGDEBUG(anna::Logger::debug(entry + " is being converted to xml", ANNA_FILE_LOCATION));
198
199       if(!getDataBlockFromHexFile(entry, db_aux))
200         _exit("Error reading hex file provided");
201
202       // Decode datablock:
203       decodeDataBlock(db_aux);
204
205       // Write conversion:
206       std::string outputFile = entry + ".as.xml";
207       std::ofstream out(outputFile.c_str(), std::ifstream::out);
208       out << G_codecMsg->asXMLString();
209       out.close();
210
211       anyHexConverted = true;
212     }
213   }
214
215   // Processing .xml files:
216   bool anyXmlConverted = false;
217   if (processXml) {
218     for (anna::io::Directory::const_iterator it = directoryXml.begin(); it != directoryXml.end(); it++) {
219       const std::string& entry = anna::io::Directory::data (it);
220       LOGDEBUG(anna::Logger::debug(entry + " is being converted to hex", ANNA_FILE_LOCATION));
221   
222       // Load file:
223       G_codecMsg->loadXML(entry);  
224       
225       // Write conversion:
226       std::string hexString = anna::functions::asHexString(G_codecMsg->code());
227       std::string outputFile = entry + ".as.hex";
228       std::ofstream out(outputFile.c_str(), std::ifstream::out);
229       out.write(hexString.c_str(), hexString.size());
230       out.close();
231   
232       anyXmlConverted = true;
233     }
234   }
235
236
237   std::string msg = "Open '"; msg += filetrace; msg += "' in order to see process traces.\n";
238   if (anyHexConverted) msg += "Open '*.hex.as.xml' files to see decoding results.\n";
239   if (anyXmlConverted) msg += "Open '*.xml.as.hex' files to see encoding results.\n";
240   _exit(msg, 0);
241 }
242