makemime.c 7.2 KB

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