makemime.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * makemime: create MIME-encoded message
  4. *
  5. * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  6. *
  7. * Licensed under GPLv2, see file LICENSE in this source tree.
  8. */
  9. //kbuild:lib-$(CONFIG_MAKEMIME) += makemime.o mail.o
  10. #include "libbb.h"
  11. #include "mail.h"
  12. #if 0
  13. # define dbg_error_msg(...) bb_error_msg(__VA_ARGS__)
  14. #else
  15. # define dbg_error_msg(...) ((void)0)
  16. #endif
  17. /*
  18. makemime -c type [-o file] [-e encoding] [-C charset] [-N name] \
  19. [-a "Header: Contents"] file
  20. -m [ type ] [-o file] [-e encoding] [-a "Header: Contents"] file
  21. -j [-o file] file1 file2
  22. @file
  23. file: filename - read or write from filename
  24. - - read or write from stdin or stdout
  25. &n - read or write from file descriptor n
  26. \( opts \) - read from child process, that generates [ opts ]
  27. Options:
  28. -c type - create a new MIME section from "file" with this
  29. Content-Type: (default is application/octet-stream).
  30. -C charset - MIME charset of a new text/plain section.
  31. -N name - MIME content name of the new mime section.
  32. -m [ type ] - create a multipart mime section from "file" of this
  33. Content-Type: (default is multipart/mixed).
  34. -e encoding - use the given encoding (7bit, 8bit, quoted-printable,
  35. or base64), instead of guessing. Omit "-e" and use
  36. -c auto to set Content-Type: to text/plain or
  37. application/octet-stream based on picked encoding.
  38. -j file1 file2 - join mime section file2 to multipart section file1.
  39. -o file - write the result to file, instead of stdout (not
  40. allowed in child processes).
  41. -a header - prepend an additional header to the output.
  42. @file - read all of the above options from file, one option or
  43. value on each line.
  44. {which version of makemime is this? What do we support?}
  45. */
  46. /* man makemime:
  47. * -c TYPE: create a (non-multipart) MIME section with Content-Type: TYPE
  48. * makemime -c TYPE [-e ENCODING] [-o OUTFILE] [-C CHARSET] [-N NAME] [-a HEADER...] FILE
  49. * The -C option sets the MIME charset attribute for text/plain content.
  50. * The -N option sets the name attribute for Content-Type:
  51. * Encoding must be one of the following: 7bit, 8bit, quoted-printable, or base64.
  52. * -m multipart/TYPE: create a multipart MIME collection with Content-Type: multipart/TYPE
  53. * makemime -m multipart/TYPE [-e ENCODING] [-o OUTFILE] [-a HEADER...] FILE
  54. * Type must be either "multipart/mixed", "multipart/alternative", or some other MIME multipart content type.
  55. * Additionally, encoding can only be "7bit" or "8bit", and will default to "8bit" if not specified.
  56. * Finally, filename must be a MIME-formatted section, NOT a regular file.
  57. * The -m option creates an initial multipart MIME collection, that contains only one MIME section, taken from filename.
  58. * The collection is written to standard output, or the pipe or to outputfile.
  59. * -j FILE1: add a section to a multipart MIME collection
  60. * makemime -j FILE1 [-o OUTFILE] FILE2
  61. * FILE1 must be a MIME collection that was previously created by the -m option.
  62. * FILE2 must be a MIME section that was previously created by the -c option.
  63. * The -j options adds the MIME section in FILE2 to the MIME collection in FILE1.
  64. */
  65. /* In busybox 1.15.0.svn, makemime generates output like this
  66. * (empty lines are shown exactly!):
  67. {headers added with -a HDR}
  68. Mime-Version: 1.0
  69. Content-Type: multipart/mixed; boundary="24269534-2145583448-1655890676"
  70. --24269534-2145583448-1655890676
  71. Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
  72. Content-Disposition: inline; filename="A"
  73. Content-Transfer-Encoding: base64
  74. ...file A contents...
  75. --24269534-2145583448-1655890676
  76. Content-Type: {set by -c, e.g. text/plain}; charset={set by -C, e.g. us-ascii}
  77. Content-Disposition: inline; filename="B"
  78. Content-Transfer-Encoding: base64
  79. ...file B contents...
  80. --24269534-2145583448-1655890676--
  81. *
  82. * For reference: here is an example email to LKML which has
  83. * 1st unnamed part (so it serves as an email body)
  84. * and one attached file:
  85. ...other headers...
  86. Content-Type: multipart/mixed; boundary="=-tOfTf3byOS0vZgxEWcX+"
  87. ...other headers...
  88. Mime-Version: 1.0
  89. ...other headers...
  90. --=-tOfTf3byOS0vZgxEWcX+
  91. Content-Type: text/plain
  92. Content-Transfer-Encoding: 7bit
  93. ...email text...
  94. ...email text...
  95. --=-tOfTf3byOS0vZgxEWcX+
  96. Content-Disposition: attachment; filename="xyz"
  97. Content-Type: text/plain; name="xyz"; charset="UTF-8"
  98. Content-Transfer-Encoding: 7bit
  99. ...file contents...
  100. ...file contents...
  101. --=-tOfTf3byOS0vZgxEWcX+--
  102. ...random junk added by mailing list robots and such...
  103. */
  104. //usage:#define makemime_trivial_usage
  105. //usage: "[OPTIONS] [FILE]..."
  106. //usage:#define makemime_full_usage "\n\n"
  107. //usage: "Create multipart MIME-encoded message from FILEs\n"
  108. /* //usage: "Transfer encoding is base64, disposition is inline (not attachment)\n" */
  109. //usage: "\n -o FILE Output. Default: stdout"
  110. //usage: "\n -a HDR Add header(s). Examples:"
  111. //usage: "\n \"From: user@host.org\", \"Date: `date -R`\""
  112. //usage: "\n -c CT Content type. Default: application/octet-stream"
  113. //usage: "\n -C CS Charset. Default: " CONFIG_FEATURE_MIME_CHARSET
  114. /* //usage: "\n -e ENC Transfer encoding. Ignored. base64 is assumed" */
  115. //usage: "\n"
  116. //usage: "\nOther options are silently ignored"
  117. /*
  118. * -c [Content-Type] should create just one MIME section
  119. * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR".
  120. * NB: without "Content-Disposition:" auto-added, unlike we do now
  121. * NB2: -c has *optional* param which nevertheless _can_ be specified after a space :(
  122. *
  123. * -m [multipart/mixed] should create multipart MIME section
  124. * with "Content-Type:", "Content-Transfer-Encoding:", and HDRs from "-a HDR",
  125. * and add FILE to it _verbatim_:
  126. * HEADERS
  127. *
  128. * --=_1_1321709112_1605
  129. * FILE_CONTENTS
  130. * --=_1_1321709112_1605
  131. * without any encoding of FILE_CONTENTS. (Basically, it expects that FILE
  132. * is the result of "makemime -c").
  133. *
  134. * -j MULTIPART_FILE1 SINGLE_FILE2 should output MULTIPART_FILE1 + SINGLE_FILE2
  135. *
  136. * Our current behavior is a mutant "-m + -c + -j" one: we create multipart MIME
  137. * and we put "-c" encoded FILEs into many multipart sections.
  138. */
  139. int makemime_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  140. int makemime_main(int argc UNUSED_PARAM, char **argv)
  141. {
  142. llist_t *opt_headers = NULL, *l;
  143. const char *opt_output;
  144. const char *content_type = "application/octet-stream";
  145. #define boundary opt_output
  146. enum {
  147. OPT_c = 1 << 0, // create (non-multipart) section
  148. OPT_e = 1 << 1, // Content-Transfer-Encoding. Ignored. Assumed base64
  149. OPT_o = 1 << 2, // output to
  150. OPT_C = 1 << 3, // charset
  151. OPT_N = 1 << 4, // COMPAT
  152. OPT_a = 1 << 5, // additional headers
  153. //OPT_m = 1 << 6, // create mutipart section
  154. //OPT_j = 1 << 7, // join section to multipart section
  155. };
  156. INIT_G();
  157. // parse options
  158. opt_complementary = "a::";
  159. opts = getopt32(argv,
  160. "c:e:o:C:N:a:", // "m:j:",
  161. &content_type, NULL, &opt_output, &G.opt_charset, NULL, &opt_headers //, NULL, NULL
  162. );
  163. //argc -= optind;
  164. argv += optind;
  165. // respect -o output
  166. if (opts & OPT_o)
  167. freopen(opt_output, "w", stdout);
  168. // no files given on command line? -> use stdin
  169. if (!*argv)
  170. *--argv = (char *)"-";
  171. // put additional headers
  172. for (l = opt_headers; l; l = l->link)
  173. puts(l->data);
  174. // make a random string -- it will delimit message parts
  175. srand(monotonic_us());
  176. boundary = xasprintf("%u-%u-%u",
  177. (unsigned)rand(), (unsigned)rand(), (unsigned)rand());
  178. // put multipart header
  179. printf(
  180. "Mime-Version: 1.0\n"
  181. "Content-Type: multipart/mixed; boundary=\"%s\"\n"
  182. , boundary
  183. );
  184. // put attachments
  185. while (*argv) {
  186. printf(
  187. "\n--%s\n"
  188. "Content-Type: %s; charset=%s\n"
  189. "Content-Disposition: inline; filename=\"%s\"\n"
  190. "Content-Transfer-Encoding: base64\n"
  191. , boundary
  192. , content_type
  193. , G.opt_charset
  194. , bb_get_last_path_component_strip(*argv)
  195. );
  196. encode_base64(*argv++, (const char *)stdin, "");
  197. }
  198. // put multipart footer
  199. printf("\n--%s--\n" "\n", boundary);
  200. return EXIT_SUCCESS;
  201. #undef boundary
  202. }