1 /*
2 * (C) 2014,2015 Jack Lloyd
3 *
4 * Botan is released under the Simplified BSD License (see license.txt)
5 */
6 
7 #include "cli.h"
8 
9 #if defined(BOTAN_HAS_COMPRESSION)
10    #include <botan/compression.h>
11    #include <fstream>
12 #endif
13 
14 namespace Botan_CLI {
15 
16 #if defined(BOTAN_HAS_COMPRESSION)
17 
18 class Compress final : public Command
19    {
20    public:
Compress()21       Compress() : Command("compress --type=gzip --level=6 --buf-size=8192 file") {}
22 
output_filename(const std::string & input_fsname,const std::string & comp_type)23       std::string output_filename(const std::string& input_fsname, const std::string& comp_type)
24          {
25          const std::map<std::string, std::string> suffixes =
26             {
27                { "zlib", "zlib" },
28                { "gzip", "gz" },
29                { "bzip2", "bz2" },
30                { "lzma", "xz" },
31             };
32 
33          auto suffix_info = suffixes.find(comp_type);
34          if(suffixes.count(comp_type) == 0)
35             {
36             throw CLI_Error_Unsupported("Compressing", comp_type);
37             }
38 
39          return input_fsname + "." + suffix_info->second;
40          }
41 
group() const42       std::string group() const override
43          {
44          return "compression";
45          }
46 
description() const47       std::string description() const override
48          {
49          return "Compress a given file";
50          }
51 
go()52       void go() override
53          {
54          const std::string comp_type = get_arg("type");
55          const size_t buf_size = get_arg_sz("buf-size");
56          const size_t comp_level = get_arg_sz("level");
57 
58          std::unique_ptr<Botan::Compression_Algorithm> compress;
59 
60          compress.reset(Botan::make_compressor(comp_type));
61 
62          if(!compress)
63             {
64             throw CLI_Error_Unsupported("Compression", comp_type);
65             }
66 
67          const std::string in_file = get_arg("file");
68          std::ifstream in(in_file, std::ios::binary);
69 
70          if(!in.good())
71             {
72             throw CLI_IO_Error("reading", in_file);
73             }
74 
75          const std::string out_file = output_filename(in_file, comp_type);
76          std::ofstream out(out_file, std::ios::binary);
77          if(!out.good())
78             {
79             throw CLI_IO_Error("writing", out_file);
80             }
81 
82          Botan::secure_vector<uint8_t> buf;
83 
84          compress->start(comp_level);
85 
86          while(in.good())
87             {
88             buf.resize(buf_size);
89             in.read(reinterpret_cast<char*>(buf.data()), buf.size());
90             buf.resize(in.gcount());
91 
92             compress->update(buf);
93 
94             out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
95             }
96 
97          buf.clear();
98          compress->finish(buf);
99          out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
100          out.close();
101          }
102    };
103 
104 BOTAN_REGISTER_COMMAND("compress", Compress);
105 
106 class Decompress final : public Command
107    {
108    public:
Decompress()109       Decompress() : Command("decompress --buf-size=8192 file") {}
110 
parse_extension(const std::string & in_file,std::string & out_file,std::string & suffix)111       void parse_extension(const std::string& in_file,
112                            std::string& out_file,
113                            std::string& suffix)
114          {
115          auto last_dot = in_file.find_last_of('.');
116          if(last_dot == std::string::npos || last_dot == 0)
117             {
118             throw CLI_Error("No extension detected in filename '" + in_file + "'");
119             }
120 
121          out_file = in_file.substr(0, last_dot);
122          suffix = in_file.substr(last_dot + 1, std::string::npos);
123          }
124 
group() const125       std::string group() const override
126          {
127          return "compression";
128          }
129 
description() const130       std::string description() const override
131          {
132          return "Decompress a given compressed archive";
133          }
134 
go()135       void go() override
136          {
137          const size_t buf_size = get_arg_sz("buf-size");
138          const std::string in_file = get_arg("file");
139          std::string out_file, suffix;
140          parse_extension(in_file, out_file, suffix);
141 
142          std::ifstream in(in_file, std::ios::binary);
143 
144          if(!in.good())
145             {
146             throw CLI_IO_Error("reading", in_file);
147             }
148 
149          std::unique_ptr<Botan::Decompression_Algorithm> decompress;
150 
151          decompress.reset(Botan::make_decompressor(suffix));
152 
153          if(!decompress)
154             {
155             throw CLI_Error_Unsupported("Decompression", suffix);
156             }
157 
158          std::ofstream out(out_file, std::ios::binary);
159          if(!out.good())
160             {
161             throw CLI_IO_Error("writing", out_file);
162             }
163 
164          Botan::secure_vector<uint8_t> buf;
165 
166          decompress->start();
167 
168          while(in.good())
169             {
170             buf.resize(buf_size);
171             in.read(reinterpret_cast<char*>(buf.data()), buf.size());
172             buf.resize(in.gcount());
173 
174             decompress->update(buf);
175 
176             out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
177             }
178 
179          buf.clear();
180          decompress->finish(buf);
181          out.write(reinterpret_cast<const char*>(buf.data()), buf.size());
182          out.close();
183          }
184    };
185 
186 BOTAN_REGISTER_COMMAND("decompress", Decompress);
187 
188 #endif
189 
190 }
191