1 /***************************************************************************
2 copyright : (C) 2002-2005 by Stefano Barbato
3 email : stefano@codesink.org
5 $Id: exbin.cxx,v 1.4 2005/03/07 16:50:59 tat Exp $
6 ***************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
17 * extract a Part based on command line parameters
26 #include <mimetic/mimetic.h>
29 using namespace mimetic;
31 unsigned int g_decoded = 0, g_messages = 0, g_files = 0, g_parts = 0;
35 cout << "exbin [params] [in_file...]" << endl;
36 cout << "\t-t type[/subtype] matches Content-Type" << endl;
37 cout << "\t-q totaly quiet; exit code = num of decoded entities" << endl;
45 : m_quiet(0), m_set(0)
48 bool operator()(const MimeEntity* pMe) const
50 const Header& h = pMe->header();
51 // if not set consider a part to be an attach if its
52 // MIME type is not text/*
54 return h.contentType().type() != "text" &&
55 h.contentType().type() != "message";
56 // check for content type match
57 if(m_type.length() && m_type != h.contentType().type())
59 if(m_subtype.length() && m_subtype != h.contentType().subtype())
63 void type(const string& s) { m_type = s; ++m_set; }
64 const istring& type() const { return m_type; }
65 void subtype(const string& s) { m_subtype = s; ++m_set; }
66 const istring& subtype() const { return m_subtype; }
67 void quiet(bool b) { m_quiet = b; }
68 bool quiet() const { return m_quiet; }
70 istring m_type, m_subtype;
74 void die(bool b, const string& msg)
78 cerr << "Error: " << msg << endl << endl;
83 string get_filename(const MimeEntity& me)
85 if(me.hasField(ContentDisposition::label))
87 const ContentDisposition& cd = me.header().contentDisposition();
88 if(cd.param("filename").length())
89 return cd.param("filename");
90 } else if (me.hasField(ContentType::label)) {
91 const ContentType& ct = me.header().contentType();
92 return string("unnamed_" + ct.type() + "." + ct.subtype());
94 return "unknown_attachment";
97 static bool is_known_mechanism(const string& mechanism)
99 istring m = mechanism;
101 if(m == "base64" || m == "quoted-printable" || m == "binary" ||
102 m == "7bit" || m == "8bit")
108 void decode_bin(const MimeEntity& me)
110 const ContentTransferEncoding& cte = me.header().contentTransferEncoding();
111 const ContentDisposition& cd = me.header().contentDisposition();
113 if(is_known_mechanism(cte.mechanism()))
115 string filename = get_filename(me);
116 if(File::exists(filename))
119 for(t = 0; File::exists(utils::int2str(t)+"-"+filename);t++)
121 filename = utils::int2str(t) + "-" + filename;
123 const ContentType& ct = me.header().contentType();
125 cout << "\tdecoding " << filename
126 << " (" << ct.type() << "/" << ct.subtype() << ")"
128 ofstream of(filename.c_str());
131 cerr << "ERR: unable to write to " << filename << endl;
134 ostreambuf_iterator<char> oi(of);
135 istring enc_algo = cte.mechanism();
136 if(enc_algo == "base64")
140 decode(me.body().begin(), me.body().end(), b64 ,oi);
141 } else if (enc_algo == "quoted-printable") {
144 decode(me.body().begin(), me.body().end(), qp, oi);
145 } else if (enc_algo == "8bit" || enc_algo == "7bit" ||
146 enc_algo == "binary") {
147 copy(me.body().begin(), me.body().end(), oi);
149 cerr << "ERR: unknown encoding algorithm "
155 void parsePart(const MimeEntity& me, string& fqn)
160 if(!mpr.quiet() && fqn.length() > 0)
167 MimeEntityList::const_iterator mbit, meit;
168 mbit = me.body().parts().begin(), meit = me.body().parts().end();
169 for(; mbit != meit; ++mbit)
170 parsePart(**mbit, fqn);
173 inline int isnl(char c)
175 return c == '\n' || c == '\r';
178 template<typename Iterator>
179 void parseMboxFile(Iterator bit, Iterator eit, string& fqn)
187 it = utils::find_bm(it, eit, "From ");
188 //it = find_n(it, eit, "From ");
189 prev = *(it-1); // previous char (must be a newline)
190 if(it == eit || isnl(prev) )
193 ++it; // From in the middle of a line
196 MimeEntity me(bit, it);
201 ++it; // skip the current From
205 template<typename Iterator>
206 void parse(Iterator bit, Iterator eit, string& fqn)
208 string sep = "From ";
209 Iterator it = utils::find_bm(bit, bit + sep.length(), sep);
212 parseMboxFile(bit, eit, fqn);
215 MimeEntity me(bit, eit);
221 int main(int argc, char** argv)
223 std::ios_base::sync_with_stdio(false);
225 // handle command line parameters
229 string param = argv[p];
232 else if (param == "-q")
234 else if (param == "-t") {
235 die( ++p == argc, param + " requires an argument");
236 ContentType ct(argv[p]);
237 die(mpr.type().length() != 0, "just one -t allowed");
239 mpr.subtype(ct.subtype());
241 // filename list starts here
242 // first filename: argv[p]
253 enum { page_sz = 4096 };
256 while((count = cin.rdbuf()->sgetn(page, page_sz)) > 0)
258 parse(buf.begin(), buf.end(), fqn);
261 for(int fc = p; fc < argc; ++fc)
267 cerr << "ERR: unable to open file " << argv[fc]
272 parse(in.begin(), in.end(), fqn);
276 cout << g_files << " file(s) analyzed, " <<
277 g_messages << " message(s) and " <<
278 g_parts << " entitie(s) parsed, " <<
279 g_decoded << " attachment(s) extracted." << endl;