ucommon  7.0.0
About: GNU uCommon C++ is a portable and optimized class framework for writing C++ applications that need to use threads and support concurrent synchronization, and that use sockets, XML parsing, object serialization, thread-optimized string and data structure classes, etc..
  Fossies Dox: ucommon-7.0.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

car.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2014 David Sugar, Tycho Softworks.
2 // Copyright (C) 2015 Cherokees of Idaho.
3 //
4 // This file is part of GNU uCommon C++.
5 //
6 // GNU uCommon C++ is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU Lesser General Public License as published
8 // by the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // GNU uCommon C++ is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public License
17 // along with GNU uCommon C++. If not, see <http://www.gnu.org/licenses/>.
18 
19 #include <ucommon/secure.h>
20 #include <sys/stat.h>
21 
22 using namespace ucommon;
23 
24 static shell::flagopt helpflag('h',"--help", _TEXT("display this list"));
25 static shell::flagopt althelp('?', NULL, NULL);
26 static shell::stringopt tag('t', "--tag", _TEXT("tag annotation"), "text", "");
27 static shell::stringopt algo('c', "--cipher", _TEXT("cipher method (aes256)"), "method", "aes256");
28 static shell::flagopt decode('d', "--decode", _TEXT("decode archive"));
29 static shell::stringopt hash('h', "--digest", _TEXT("digest method (sha)"), "method", "sha");
30 static shell::flagopt noheader('n', "--no-header", _TEXT("without wrapper"));
31 static shell::stringopt out('o', "--output", _TEXT("output file"), "filename", "-");
32 static shell::flagopt quiet('q', "--quiet", _TEXT("quiet operation"));
33 static shell::flagopt recursive('R', "--recursive", _TEXT("recursive directory scan"));
34 static shell::flagopt altrecursive('r', NULL, NULL);
35 static shell::flagopt hidden('s', "--hidden", _TEXT("include hidden files"));
36 static shell::flagopt yes('y', "--overwrite", _TEXT("overwrite existing files"));
37 
38 static bool binary = false;
39 static int exit_code = 0;
40 static const char *argv0 = "car";
41 static uint8_t frame[48], cbuf[48];
43 static FILE *output = stdout;
44 static enum {d_text, d_file, d_scan, d_init} decoder = d_init;
45 static unsigned frames;
46 
47 static void report(const char *path, int code)
48 {
49  const char *err = _TEXT("i/o error");
50 
51  switch(code) {
52  case EACCES:
53  case EPERM:
54  err = _TEXT("permission denied");
55  break;
56  case EROFS:
57  err = _TEXT("read-only file system");
58  break;
59  case ENODEV:
60  case ENOENT:
61  err = _TEXT("no such file or directory");
62  break;
63  case ENOTDIR:
64  err = _TEXT("not a directory");
65  break;
66  case ENOTEMPTY:
67  err = _TEXT("directory not empty");
68  break;
69  case ENOSPC:
70  err = _TEXT("no space left on device");
71  break;
72  case EBADF:
73  case ENAMETOOLONG:
74  err = _TEXT("bad file path");
75  break;
76  case EBUSY:
77  case EINPROGRESS:
78  err = _TEXT("file or directory busy");
79  break;
80  case EINTR:
81  err = _TEXT("operation interupted");
82  break;
83  case EISDIR:
84  err = _TEXT("is a directory");
85  break;
86 #ifdef ELOOP
87  case ELOOP:
88  err = _TEXT("too many sym links");
89  break;
90 #endif
91  }
92 
93  if(path)
94  shell::printf("%s: %s: %s\n", argv0, path, err);
95  else
96  shell::errexit(1, "*** %s: %s\n", argv0, err);
97 
98  exit_code = 1;
99 }
100 
101 static bool encode(const char *path, FILE *fp, size_t offset = 0)
102 {
103  memset(frame, 0, sizeof(frame));
104 
105  size_t count = fread(frame + offset, 1, sizeof(frame) - offset, fp);
106  char buffer[128];
107 
108  if(ferror(fp)) {
109  report(path, errno);
110  return false;
111  }
112 
113  if(count)
114  count += offset;
115 
116  // add padd value for last frame...
117  if(count < sizeof(frame))
118  frame[sizeof(frame) - 1] = (char)(count - offset);
119 
120  // cipher it...
121  size_t encoded = cipher.put(frame, sizeof(frame));
122  if(encoded != sizeof(frame)) {
123  report(path, EINTR);
124  return false;
125  }
126 
127  if(binary)
128  fwrite(cbuf, sizeof(cbuf), 1, output);
129  else {
131  fprintf(output, "%s\n", buffer);
132  }
133 
134  // return status
135  if(count == sizeof(frame))
136  return true;
137 
138  return false;
139 }
140 
141 static void encodestream(void)
142 {
143 
144  size_t offset = 6;
145 
147  fputs("car: type your message\n", stderr);
148 
149  memset(frame, 0, sizeof(frame));
150 
151  for(;;) {
152  if(!encode("-", stdin, offset))
153  break;
154  offset = 0;
155  }
156 
157  if(!binary && !is(noheader))
158  fprintf(output, "-----END CAR STREAM-----\n");
159 }
160 
161 static void header(void)
162 {
163  binary = true;
164 
165  memset(frame, 0, sizeof(frame));
166  String::set((char *)frame, 6, ".car");
167  frame[5] = 1;
168  frame[4] = 0xff;
169  String::set((char *)frame + 6, sizeof(frame) - 6, *tag);
170  fwrite(frame, sizeof(frame), 1, output);
171 }
172 
173 static void encodefile(const char *path, const char *name)
174 {
175  char buffer[128];
176 
177  fsys::fileinfo_t ino;
178 
179  fsys::info(path, &ino);
180 
181  FILE *fp = fopen(path, "r");
182  if(!fp) {
183  report(name, errno);
184  return;
185  }
186 
187  lsb_setlong(frame, ino.st_size);
188  frame[4] = 1;
189  frame[5] = 0;
190  String::set((char *)(frame + 6), sizeof(frame) - 6, name);
191 
192  cipher.put(frame, sizeof(frame));
193 
194  if(binary)
195  fwrite(cbuf, sizeof(cbuf), 1, output);
196  else {
198  fprintf(output, "%s\n", buffer);
199  }
200 
201  for(;;) {
202  if(!encode(name, fp))
203  break;
204  }
205  fclose(fp);
206 }
207 
208 static void final(void)
209 {
210  size_t size;
211 
212  switch(decoder) {
213  case d_init:
214  return;
215  case d_scan:
216  cipher.put(frame, sizeof(frame));
217  if(cbuf[4] > 0)
218  return;
219  size = cbuf[sizeof(frame) - 1];
220  if(size)
221  fwrite(cbuf + 6, size, 1, output);
222  break;
223  default:
224  decoder = d_scan;
225  cipher.put(frame, sizeof(frame));
226  size = cbuf[sizeof(frame) - 1];
227  if(size)
228  fwrite(cbuf, size, 1, output);
229  }
230 }
231 
232 static void process(void)
233 {
234  string_t path;
235  int key;
236  char *cp;
237 
238  switch(decoder) {
239  case d_init:
240  decoder = d_scan;
241  return;
242  case d_scan:
243  cipher.put(frame, sizeof(frame));
244  if(cbuf[4] == 0xff) // header...
245  break;
246  if(!cbuf[4]) { // if message
247  fwrite(cbuf + 6, sizeof(frame) - 6, 1, output);
248  decoder = d_text;
249  break;
250  }
251  decoder = d_file;
252  frames = lsb_getlong(cbuf) / sizeof(frame);
253  path = str((char *)(cbuf + 6));
254  cp = strrchr((char *)(cbuf + 6), '/');
255  if(cp) {
256  *cp = 0;
257  dir::create((char *)(cbuf + 6), 0640);
258  }
259  if(fsys::is_dir(*path))
260  shell::errexit(8, "*** %s: %s: %s\n",
261  argv0, *path, _TEXT("output is directory"));
262 
263  if(fsys::is_file(*path) && !is(yes)) {
264  string_t prompt = str("overwrite ") + path + " <y/n>? ";
265  if(is(quiet))
266  key = 0;
267  else
268  key = shell::inkey(prompt);
269  switch(key)
270  {
271  case 'y':
272  case 'Y':
273  printf("y\n");
274  break;
275  default:
276  printf("n\n");
277  case 0:
278  shell::errexit(8, "*** %s: %s: %s\n",
279  argv0, *path, _TEXT("will not overwrite"));
280  }
281  }
282 
283  output = fopen(*path, "w");
284  if(!output)
285  shell::errexit(8, "*** %s: %s: %s\n",
286  argv0, *path, _TEXT("cannot create"));
287  if(!is(quiet))
288  printf("decoding %s...\n", *path);
289  break;
290  case d_file:
291  if(!frames) {
292  final();
293  break;
294  }
295  --frames;
296  default:
297  cipher.put(frame, sizeof(frame));
298  fwrite(cbuf, sizeof(frame), 1, output);
299  }
300 }
301 
302 static void binarydecode(FILE *fp, const char *path)
303 {
304  char buffer[48];
305 
306  memset(frame, 0, sizeof(frame));
307  memset(buffer, 0, sizeof(buffer));
308  if(fread(frame, sizeof(frame), 1, fp) < 1)
309  shell::errexit(6, "*** %s: %s: %s\n",
310  argv0, path, _TEXT("cannot read archive"));
311 
312  if(!eq((char *)frame, ".car", 4) || (frame[4] != 0xff))
313  shell::errexit(6, "*** %s: %s: %s\n",
314  argv0, path, _TEXT("not a cryptographic archive"));
315 
316  for(;;) {
317  if(feof(fp)) {
318  final();
319  return;
320  }
321 
322  if(ferror(fp)) {
323  exit_code = errno;
324  report(path, exit_code);
325  return;
326  }
327 
328  if(fread(buffer, sizeof(buffer), 1, fp) < 1)
329  continue;
330  process();
331  memcpy(frame, buffer, sizeof(frame));
332  }
333 }
334 
335 static void streamdecode(FILE *fp, const char *path)
336 {
337  char buffer[128];
338  for(;;) {
339  if(NULL == fgets(buffer, sizeof(buffer), fp) || feof(fp)) {
340  shell::errexit(5, "*** %s: %s: %s\n",
341  argv0, path, _TEXT("no archive found"));
342  }
343  if(ferror(fp)) {
344  exit_code = errno;
345  report(path, exit_code);
346  return;
347  }
348  if(eq("-----BEGIN CAR STREAM-----\n", buffer))
349  break;
350  }
351  for(;;) {
352  if(feof(fp)) {
353  final();
354  return;
355  }
356 
357  if(ferror(fp)) {
358  exit_code = errno;
359  report(path, exit_code);
360  return;
361  }
362 
363  if(fgets(buffer, sizeof(buffer), fp) == NULL)
364  continue;
365 
366  if(eq("-----END CAR STREAM-----\n", buffer)) {
367  final();
368  return;
369  }
370 
371  // ignore extra headers...
372  if(strstr(buffer, ": "))
373  continue;
374 
375  process();
376  unsigned len = String::b64decode(frame, buffer, 48);
377  if(len < 48) {
378  report(path, EINTR);
379  return;
380  }
381  }
382 }
383 
384 static void scan(string_t path, string_t prefix)
385 {
386  char filename[128];
387  string_t filepath;
388  string_t name;
389  string_t subdir;
390  dir_t dir(path);
391 
392  while(is(dir) && dir.read(filename, sizeof(filename))) {
393  if(*filename == '.' && (filename[1] == '.' || !filename[1]))
394  continue;
395 
396  if(*filename == '.' && !is(hidden))
397  continue;
398 
399  filepath = str(path) + str("/") + str(filename);
400  if(prefix[0])
401  name ^= prefix + str("/") + str(filename);
402  else
403  name ^= str(filename);
404 
405  if(fsys::is_dir(filepath)) {
406  if(is(recursive) || is(altrecursive))
407  scan(filepath, name);
408  else
409  report(*filepath, EISDIR);
410  }
411  else
412  encodefile(*filepath, *name);
413  }
414 }
415 
416 int main(int argc, char **argv)
417 {
418  shell::bind("car");
419  shell args(argc, argv);
420  argv0 = args.argv0();
421  unsigned count = 0;
422  char passphrase[256];
423  char confirm[256];
424  const char *ext;
425 
426  argv0 = args.argv0();
427 
428  if(is(helpflag) || is(althelp)) {
429  printf("%s\n", _TEXT("Usage: car [options] path..."));
430  printf("%s\n\n", _TEXT("Crytographic archiver"));
431  printf("%s\n", _TEXT("Options:"));
432  shell::help();
433  printf("\n%s\n", _TEXT("Report bugs to dyfet@gnu.org"));
434  return 0;
435  }
436 
437  if(!secure::init())
438  shell::errexit(1, "*** %s: %s\n", argv0, _TEXT("not supported"));
439 
440  if(!Digest::has(*hash))
441  shell::errexit(2, "*** %s: %s: %s\n",
442  argv0, *hash, _TEXT("unkown or unsupported digest method"));
443 
444  if(!Cipher::has(*algo))
445  shell::errexit(2, "*** %s: %s: %s\n",
446  argv0, *algo, _TEXT("unknown or unsupported cipher method"));
447 
448  shell::getpass("passphrase: ", passphrase, sizeof(passphrase));
449  shell::getpass("confirm: ", confirm, sizeof(confirm));
450 
451  if(!eq(passphrase, confirm))
452  shell::errexit(3, "*** %s: %s\n",
453  argv0, _TEXT("passphrase does not match confirmation"));
454 
455  // set key and cleanup buffers...
456  skey_t key(*algo, *hash, passphrase);
457  memset(passphrase, 0, sizeof(passphrase));
458  memset(confirm, 0, sizeof(confirm));
459  memset(cbuf, 0, sizeof(cbuf));
460 
461  if(is(decode))
462  cipher.set(&key, Cipher::DECRYPT, cbuf, sizeof(cbuf));
463  else
464  cipher.set(&key, Cipher::ENCRYPT, cbuf, sizeof(cbuf));
465 
466  if(is(decode) && !args()) {
467  streamdecode(stdin, "-");
468  goto end;
469  }
470 
471  if(is(decode) && args() > 1)
472  shell::errexit(3, "*** %s: %s\n", argv0, _TEXT("specify only one file for decoder"));
473 
474  if(is(decode)) {
475  FILE *fp = fopen(args[0], "r");
476  if(!fp)
477  shell::errexit(7, "*** %s: %s: %s\n",
478  argv0, args[0], _TEXT("cannot open or access"));
479 
480  const char *ext = strrchr(args[0], '.');
481  if(eq_case(ext, ".car"))
482  binarydecode(fp, args[0]);
483  else
484  streamdecode(fp, args[0]);
485  fclose(fp);
486  goto end;
487  }
488 
489  if(!eq(*out, "-")) {
490  output = fopen(*out, "w");
491  if(!output) {
492  shell::errexit(4, "*** %s: %s: %s\n",
493  argv0, *out, _TEXT("cannot create"));
494  }
495  }
496 
497  // if we are outputting to a car file, do it in binary
498  ext = strrchr(*out, '.');
499  if(eq_case(ext, ".car"))
500  header();
501 
502  if(!binary && !is(noheader)) {
503  fprintf(output, "-----BEGIN CAR STREAM-----\n");
504  if(tag)
505  fprintf(output, "Tag: %s\n", *tag);
506  }
507 
508  if(!args()) {
509  encodestream();
510  goto end;
511  }
512 
513  while(count < args()) {
514  if(fsys::is_dir(args[count]))
515  scan(str(args[count++]), "");
516  else {
517  const char *cp = args[count++];
518  const char *ep = strrchr(cp, '/');
519  if(!ep)
520  ep = strrchr(cp, '\\');
521  if(ep)
522  ++ep;
523  else
524  ep = cp;
525  encodefile(cp, ep);
526  }
527  }
528 
529  if(!binary && !is(noheader))
530  fprintf(output, "-----END CAR STREAM-----\n");
531 
532 end:
533  return exit_code;
534 }
535 
ucommon::String::b64encode
static size_t b64encode(char *string, const uint8_t *binary, size_t size, size_t width=0)
Definition: string.cpp:2126
ucommon::Cipher::DECRYPT
Definition: secure.h:292
ucommon::String::b64decode
static size_t b64decode(uint8_t *binary, const char *string, size_t size, bool ws=false)
Definition: string.cpp:2226
frames
static unsigned frames
Definition: car.cpp:45
ucommon::Cipher::set
void set(uint8_t *address, size_t size=0)
Definition: common.cpp:633
argv0
static const char * argv0
Definition: car.cpp:40
ucommon::fsys::fileinfo_t
struct stat fileinfo_t
Definition: fsys.h:147
ucommon::shell::getpass
static char * getpass(const char *prompt, char *buffer, size_t size)
Definition: shell.cpp:1483
out
static shell::stringopt out('o', "--output", _TEXT("output file"), "filename", "-")
ucommon::shell::bind
static void bind(const char *name)
Definition: shell.cpp:2113
ucommon::eq_case
bool eq_case(char const *s1, char const *s2)
Definition: string.h:1699
ucommon::Cipher::Key
Definition: secure.h:301
d_file
Definition: car.cpp:44
ucommon
Definition: access.cpp:23
ucommon::fsys::is_tty
bool is_tty(void) const
Definition: fsys.cpp:801
tag
static shell::stringopt tag('t', "--tag", _TEXT("tag annotation"), "text", "")
recursive
static shell::flagopt recursive('R', "--recursive", _TEXT("recursive directory scan"))
ucommon::String
Definition: string.h:78
altrecursive
static shell::flagopt altrecursive('r', NULL, NULL)
ucommon::secure::init
static bool init(void)
Definition: secure.cpp:37
ucommon::Digest::has
static bool has(const char *name)
Definition: digest.cpp:69
helpflag
static shell::flagopt helpflag('h',"--help", _TEXT("display this list"))
d_init
Definition: car.cpp:44
ucommon::Cipher::put
size_t put(const uint8_t *data, size_t size)
Definition: cipher.cpp:266
ucommon::shell::inkey
static int inkey(const char *prompt=NULL)
Definition: shell.cpp:1506
exit_code
static int exit_code
Definition: car.cpp:39
binarydecode
static void binarydecode(FILE *fp, const char *path)
Definition: car.cpp:302
hash
static shell::stringopt hash('h', "--digest", _TEXT("digest method (sha)"), "method", "sha")
decoder
static enum @30 decoder
output
static FILE * output
Definition: car.cpp:43
ucommon::shell::input
static fd_t input(void)
Definition: shell.h:869
lsb_setlong
void lsb_setlong(uint8_t *b, uint32_t v)
Definition: cpr.cpp:157
algo
static shell::stringopt algo('c', "--cipher", _TEXT("cipher method (aes256)"), "method", "aes256")
ucommon::Cipher::has
static bool has(const char *name)
Definition: cipher.cpp:240
ucommon::dir::create
static int create(const char *path, unsigned mode)
Definition: fsys.cpp:895
althelp
static shell::flagopt althelp('?', NULL, NULL)
encode
static bool encode(const char *path, FILE *fp, size_t offset=0)
Definition: car.cpp:101
ucommon::shell::errexit
static void static void errexit(int exitcode, const char *format=NULL,...) __PRINTF(2
Definition: shell.cpp:828
main
int main(int argc, char **argv)
Definition: car.cpp:416
ucommon::fsys::info
int info(fileinfo_t *buffer)
lsb_getlong
uint32_t lsb_getlong(uint8_t *b)
Definition: cpr.cpp:123
scan
static void scan(string_t path, string_t prefix)
Definition: car.cpp:384
ucommon::shell
Definition: shell.h:59
d_scan
Definition: car.cpp:44
ucommon::eq
bool eq(const struct sockaddr *s1, const struct sockaddr *s2)
Definition: socket.h:2100
process
static void process(void)
Definition: car.cpp:232
ucommon::String::set
void set(const char *text)
Definition: string.cpp:802
cbuf
static uint8_t cbuf[48]
Definition: car.cpp:41
header
static void header(void)
Definition: car.cpp:161
cipher
static cipher_t cipher
Definition: car.cpp:42
secure.h
d_text
Definition: car.cpp:44
yes
static shell::flagopt yes('y', "--overwrite", _TEXT("overwrite existing files"))
quiet
static shell::flagopt quiet('q', "--quiet", _TEXT("quiet operation"))
ucommon::dir
Definition: fsys.h:743
ucommon::str
String str(Socket &so, size_t size)
Definition: socket.cpp:3507
ucommon::fsys::is_dir
static bool is_dir(const char *path)
Definition: fsys.cpp:1500
buffer
static uint8_t buffer[65536]
Definition: zerofill.cpp:27
streamdecode
static void streamdecode(FILE *fp, const char *path)
Definition: car.cpp:335
ucommon::dir::read
ssize_t read(char *buffer, size_t count)
Definition: fsys.cpp:736
encodefile
static void encodefile(const char *path, const char *name)
Definition: car.cpp:173
encodestream
static void encodestream(void)
Definition: car.cpp:141
ucommon::shell::argv0
const char * argv0() const
Definition: shell.h:664
report
static void report(const char *path, int code)
Definition: car.cpp:47
prefix
static char prefix[80]
Definition: args.cpp:33
binary
static bool binary
Definition: car.cpp:38
decode
static shell::flagopt decode('d', "--decode", _TEXT("decode archive"))
frame
static uint8_t frame[48]
Definition: car.cpp:41
hidden
static shell::flagopt hidden('s', "--hidden", _TEXT("include hidden files"))
ucommon::Cipher
Definition: secure.h:289
ucommon::shell::stringopt
Definition: shell.h:295
ucommon::shell::flagopt
Definition: shell.h:234
noheader
static shell::flagopt noheader('n', "--no-header", _TEXT("without wrapper"))
ucommon::shell::help
static void help(void)
Definition: shell.cpp:408
ucommon::shell::printf
static size_t printf(const char *format,...) __PRINTF(1
Definition: shell.cpp:874
ucommon::fsys::is_file
static bool is_file(const char *path)
Definition: fsys.cpp:1453
ucommon::Cipher::ENCRYPT
Definition: secure.h:292
ucommon::is
bool is(T &object)
Definition: generics.h:292
ucommon::_TEXT
const char * _TEXT(const char *s)
Definition: shell.h:911