1 | #include "cmdparser.hpp"
|
2 | #include <cstring>
|
3 |
|
4 | namespace fs = boost::filesystem;
|
5 |
|
6 | using namespace std;
|
7 |
|
8 |
|
9 | Configuration::Configuration() :
|
10 | input_file(),
|
11 | output_dir(),
|
12 | unbitstuff(false),
|
13 | unbitstuff_char('>'),
|
14 | unbitstuff_targets(">"),
|
15 | trailing_space(true)
|
16 | {}
|
17 |
|
18 |
|
19 | Cmdline_parser::Cmdline_parser() {}
|
20 |
|
21 |
|
22 | Configuration*
|
23 | Cmdline_parser::parse(const int argc, const char* argv[]) {
|
24 |
|
25 | static const struct poptOption options[] =
|
26 | {
|
27 | {"unbitstuff-char", 'c', POPT_ARG_STRING1U, NULL__null, 'c'},
|
28 | {"output-dir", 'd', POPT_ARG_STRING1U, NULL__null, 'd'},
|
29 | {"input-file", 'f', POPT_ARG_STRING1U, NULL__null, 'f'},
|
30 | {"help", 'h', POPT_ARG_NONE0U, NULL__null, 'h'},
|
31 | {"notrailing-space", 's', POPT_ARG_NONE0U, NULL__null, 's'},
|
32 | {"unbitstuff-targets", 't', POPT_ARG_STRING1U, NULL__null, 't'},
|
33 | {"unbitstuff", 'u', POPT_ARG_NONE0U, NULL__null, 'u'},
|
34 | {NULL__null, 0, 0, NULL__null, 0}
|
35 | };
|
36 |
|
37 |
|
38 | const char** alias_tb_argv = new const char*[6];
|
39 | alias_tb_argv[0] = "-u";
|
40 | alias_tb_argv[1] = "-s";
|
41 | alias_tb_argv[2] = "-c";
|
42 | alias_tb_argv[3] = " ";
|
43 | alias_tb_argv[4] = "-t";
|
44 | alias_tb_argv[5] = " >";
|
45 |
|
46 | static const struct poptAlias alias_tb =
|
47 | {"thunderbird", '\0', 6, alias_tb_argv};
|
48 |
|
49 | poptContext context = poptGetContext("mbox2eml", argc, argv, options, 0);
|
50 |
|
51 | int c = poptAddAlias(context, alias_tb, 0);
|
| Value stored to 'c' during its initialization is never read |
52 |
|
53 | Configuration* config = new Configuration();
|
54 |
|
55 |
|
56 | while((c = poptGetNextOpt(context)) > 0) {
|
57 | const char *optarg = poptGetOptArg(context);
|
58 | switch(c) {
|
59 | case 'c':
|
60 | if(strlen(optarg) != 1) {
|
61 | cerr << "-c: argument must be exactly ONE character!" << endl;
|
62 | exit(1);
|
63 | }
|
64 | config->unbitstuff_char = optarg[0];
|
65 | #ifdef CMDDEBUG
|
66 | cout << "option: " << (char)c << " argument: " << optarg << endl;
|
67 | #endif //CMDDEBUG
|
68 | break;
|
69 | case 'd':
|
70 | config->output_dir = check_dir(optarg);
|
71 | #ifdef CMDDEBUG
|
72 | cout << "option: " << (char)c << " argument: " << optarg << endl;
|
73 | #endif //CMDDEBUG
|
74 | break;
|
75 | case 'f':
|
76 | config->input_file = check_file(optarg);
|
77 | #ifdef CMDDEBUG
|
78 | cout << "option: " << (char)c << " argument: " << optarg << endl;
|
79 | #endif //CMDDEBUG
|
80 | break;
|
81 | case 'h':
|
82 | #ifdef CMDDEBUG
|
83 | cout << "option: " << (char)c << endl;
|
84 | #endif //CMDDEBUG
|
85 | cout << usage() << endl;
|
86 | exit(0);
|
87 | case 's':
|
88 | config->trailing_space = false;
|
89 | #ifdef CMDDEBUG
|
90 | cout << "option: " << (char)c << endl;
|
91 | #endif //CMDDEBUG
|
92 | break;
|
93 | case 't':
|
94 | config->unbitstuff_targets = optarg;
|
95 | #ifdef CMDDEBUG
|
96 | cout << "option: " << (char)c << " argument: " << optarg << endl;
|
97 | #endif //CMDDEBUG
|
98 | break;
|
99 | case 'u':
|
100 | config->unbitstuff = true;
|
101 | #ifdef CMDDEBUG
|
102 | cout << "option: " << (char)c << endl;
|
103 | #endif //CMDDEBUG
|
104 | break;
|
105 | default:
|
106 | cerr << "unknown option: " << (char)c << endl;
|
107 | exit(1);
|
108 | }
|
109 | }
|
110 |
|
111 |
|
112 | if(c < -1) {
|
113 | cerr << poptBadOption(context, 0) << ": " << poptStrerror(c) << endl;
|
114 | }
|
115 |
|
116 |
|
117 | if(poptPeekArg(context) != NULL__null) {
|
118 | cerr << "No arguments without option switch allowed:" << endl;
|
119 | const char *xtra_arg;
|
120 | while((xtra_arg = poptGetArg(context)) != NULL__null) {
|
121 | cerr << xtra_arg << endl;
|
122 | }
|
123 | }
|
124 |
|
125 | poptFreeContext(context);
|
126 |
|
127 |
|
128 | if(!check_necessary_options_set(config)) {
|
129 | cerr << usage(true);
|
130 | exit(1);
|
131 | }
|
132 |
|
133 | return config;
|
134 | }
|
135 |
|
136 |
|
137 | bool
|
138 | Cmdline_parser::check_necessary_options_set(Configuration* config) {
|
139 | bool result = true;
|
140 |
|
141 | if(config->input_file.empty()) {
|
142 | cerr << "You must specify an input-file!" << endl;
|
143 | result = false;
|
144 | }
|
145 |
|
146 | if(config->output_dir.empty()) {
|
147 | cerr << "You must specify an output-directory!" << endl;
|
148 | result = false;
|
149 | }
|
150 |
|
151 | return result;
|
152 | }
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 | fs::path
|
165 | Cmdline_parser::check_dir(const char* dir_str) {
|
166 |
|
167 | fs::path full_path;
|
168 | try {
|
169 | fs::path dir_path(dir_str);
|
170 | full_path = fs::system_complete(dir_path);
|
171 | }
|
172 | catch(...) {
|
173 | cerr << "Directory name is not valid: " << dir_str << endl;
|
174 | exit(1);
|
175 | }
|
176 |
|
177 | bool exists = false;
|
178 | try {
|
179 | exists = fs::exists(full_path);
|
180 | }
|
181 | catch(const fs::filesystem_error& ex) {
|
182 | cerr << "Could not check if dir exists: " << ex.what() << endl;
|
183 | exit(1);
|
184 | }
|
185 | if(!exists) {
|
186 | try {
|
187 | exists = fs::create_directory(full_path);
|
188 | }
|
189 | catch(...) {}
|
190 | if(!exists) {
|
191 | cerr << "Could not create directory: " << full_path << endl;
|
192 | exit(1);
|
193 | }
|
194 | }
|
195 |
|
196 | if(!fs::is_directory(full_path)) {
|
197 | cerr << "This is a file and not a directory: " << full_path << endl;
|
198 | exit(1);
|
199 | }
|
200 |
|
201 | return full_path;
|
202 | }
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | fs::path
|
210 | Cmdline_parser::check_file(const char* file_str) {
|
211 |
|
212 | fs::path full_path;
|
213 | try {
|
214 | fs::path file_path(file_str);
|
215 | full_path = fs::system_complete(file_path);
|
216 | }
|
217 | catch(...) {
|
218 | cerr << "File name is not valid: " << file_str << endl;
|
219 | exit(1);
|
220 | }
|
221 |
|
222 | try {
|
223 | if (!fs::exists(full_path)) {
|
224 | cerr << "File does not exist: " << full_path << endl;
|
225 | exit(1);
|
226 | }
|
227 | }
|
228 | catch(const fs::filesystem_error& ex) {
|
229 | cerr << "Could not check if file exists: " << ex.what() << endl;
|
230 | exit(1);
|
231 | }
|
232 |
|
233 | if(fs::is_directory(full_path)) {
|
234 | cerr << "This is a directory and not a file: " << full_path << endl;
|
235 | exit(1);
|
236 | }
|
237 |
|
238 | return full_path;
|
239 | }
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 | string
|
246 | Cmdline_parser::usage(bool oneliner) {
|
247 | ostringstream os;
|
248 |
|
249 | os << "Usage: mbox2eml [options] -f MBOX-FILE -d DEST-DIR\n";
|
250 | if(oneliner) {
|
251 | return os.str();
|
252 | }
|
253 |
|
254 | os << "Obligatory Options:\n"
|
255 | << " --output-dir | -d :\n"
|
256 | << " This is the output directory, where the eml-files are being saved.\n"
|
257 | << " --input-file | -f :\n"
|
258 | << " This is the input file which shall be split into eml-files.\n"
|
259 | << " This file MUST be in mbox format as described here:\n"
|
260 | << " http://www.die.net/doc/linux/man/man5/mbox.5.html\n"
|
261 | << "Optional Options:\n"
|
262 | << " --help | -h :\n"
|
263 | << " Displays this text.\n"
|
264 | << "Advanced Options:\n"
|
265 | << "THESE OPTIONS ARE VERY DANGEROUS!!!\n"
|
266 | << "PLEASE READ THE README-FILE VERY CAREFULLY.\n"
|
267 | << " --unbitstuff | -u :\n"
|
268 | << " Turns on the 'unbitstuff' functionality.\n"
|
269 | << " --unbitstuff-char | -c :\n"
|
270 | << " This is the character, which will be removed when 'unbitstuff'ing.\n"
|
271 | << " Default: '>'\n"
|
272 | << " --notrailing-space | -s :\n"
|
273 | << " This option triggers if a trailing space after 'From' is necessary.\n"
|
274 | << " Default: trailing space is necessary\n"
|
275 | << " --unbitstuff-targets | -t :\n"
|
276 | << " These are the characters which may be between the 'unbitstuff-char'\n"
|
277 | << " and 'From'. If only these characters are found, the affected line is\n"
|
278 | << " 'unbitstuff'ed.\n"
|
279 | << " Default: \">\"\n"
|
280 | << " --thunderbird:\n"
|
281 | << " This is an alias for -u -s -c \" \" -t \" >\"\n"
|
282 | << " This option enables the 'unbitstuff'ing for mbox-files produced by\n"
|
283 | << " Mozilla Thunderbird.\n"
|
284 | << " Thundebird violates the common mbox practice to bitstuff every line\n"
|
285 | << " starting with '[>]*From ' with a single '>'. Instead of this,\n"
|
286 | << " Thunderbird bitstuffs every line starting with '[ >]*From' with a\n"
|
287 | << " whitespace.\n"
|
288 | << " Using this option may corrupt digitally signed (i.e. S/MIME or PGP/GPG)\n"
|
289 | << " mails that have been encrypted using Thunderbird or EnigMail as\n"
|
290 | << " Thunderbird is internally working with the 'false' bitstuffed\n"
|
291 | << " mbox-format. So, if you try to fix this wrong format, you will modify\n"
|
292 | << " the content of the mail but the signature will not reflect the\n"
|
293 | << " modifications. And so the signature will be invalid.\n";
|
294 |
|
295 | return os.str();
|
296 | }
|
297 |
|