Bug Summary

File:cmdparser.cpp
Location:line 51, column 9
Description:Value stored to 'c' during its initialization is never read

Annotated Source Code

1#include "cmdparser.hpp"
2#include <cstring>
3
4namespace fs = boost::filesystem;
5
6using namespace std;
7
8/* This class holds the complete configuration for mbox2eml. */
9Configuration::Configuration() :
10 input_file(),
11 output_dir(),
12 unbitstuff(false),
13 unbitstuff_char('>'),
14 unbitstuff_targets(">"),
15 trailing_space(true)
16 {}
17
18/* constructs the command line parser */
19Cmdline_parser::Cmdline_parser() {}
20
21/* Here, the initialization of popt and all the parsing is done.*/
22Configuration*
23Cmdline_parser::parse(const int argc, const char* argv[]) {
24 // All available options.
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 // The -thunderbird shortcut.
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 // Get the context.
49 poptContext context = poptGetContext("mbox2eml", argc, argv, options, 0);
50 // Add the -thunderbird shortcut.
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 // parse the command line options
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 // error handling
112 if(c < -1) {
113 cerr << poptBadOption(context, 0) << ": " << poptStrerror(c) << endl;
114 }
115
116 // Leftover Arguments
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 // assure that all obligatory Options are set correctly
128 if(!check_necessary_options_set(config)) {
129 cerr << usage(true);
130 exit(1);
131 }
132
133 return config;
134}
135
136/* Here, all obligatory options are checked for existance. */
137bool
138Cmdline_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/* This function checks the submitted directory name for:
155 * - correctness of the name
156 * - existance
157 * If the directory does not exist and the name is valid,
158 * a new dirctory is created.
159 *
160 * If the directory name exists but is no directory, an error is raised.
161 *
162 * Returns: the verified path of the directory
163 */
164fs::path
165Cmdline_parser::check_dir(const char* dir_str) {
166 // compose the output directory's path
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 // create the output directory if necessary
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 // does the ouput path represent a directory?
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/* This function checks the submitted file name for existance.
205 * If the file name exists but is no file, an error is raised.
206 *
207 * Returns: the verified path of the file
208 */
209fs::path
210Cmdline_parser::check_file(const char* file_str) {
211 // compose the input file's path
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 // does the input file exist?
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 // is it really a file?
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/* returns the usage instructions
242 *
243 * if oneliner==true only one line is returned
244 */
245string
246Cmdline_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