mailto-form.pl 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. #! /usr/bin/perl -w
  2. # Some scripts for handling mailto URLs within lynx via an interactive form
  3. #
  4. # Warning: this is a quick demo, to show what kinds of things are possible
  5. # by hooking some external commands into lynx. Use at your own risk.
  6. #
  7. # Requirements:
  8. #
  9. # - Perl and CGI.pm.
  10. # - A "sendmail" command for actually sending mail (if you need some
  11. # other interface, change the code below in sub sendit appropriately).
  12. # - Lynx compiled with support for lynxcgi, that means EXEC_CGI must have
  13. # been defined at compilation, usually done with
  14. # ./configure --enable-cgi-links
  15. # - Lynx must have support for CERN-style rules as of 2.8.3, which must
  16. # not have been disabled at compilation (it is enabled by default).
  17. #
  18. # Instructions:
  19. # (This is for people without lynxcgi experience; if you are already
  20. # use lynxcgi, you don't have to follow everything literally, use
  21. # common sense for picking appropriate file locations in your situation.)
  22. #
  23. # - Make a subdirectory 'lynxcgi' under you home directory, i.e.
  24. # mkdir ~/lynxcgi
  25. # - Put this three script file mailto-form.pl there and make it
  26. # executable. For example,
  27. # cp mailto-form.pl ~/lynxcgi
  28. # chmod a+x ~/lynxcgi/mailto-form.pl
  29. # - Edit mailto-form.pl (THIS FILE), there are some strings that
  30. # that need to be changed, see ### Configurable variables ###
  31. # below.
  32. # - Allow lynx to execute lynxcgi files in that directory, for example,
  33. # put in your lynx.cfg file:
  34. # TRUSTED_LYNXCGI:<tab>/home/myhomedir/lynxcgi/mailto-form.pl
  35. # where <tab> is a real TAB character and you have to put the real
  36. # location of your directory in place of "myhomedir", of course.
  37. # The '~' abbreviation cannot be used.
  38. # You could also just enable execution of all lynxcgi scripts, by
  39. # not having any TRUSTED_LYNXCGI options in lynx.cfg at all, but
  40. # that can't be recommended.
  41. # - Tell lynx to actually use the lynxcgi scripts for mailto URLs.
  42. # There are two variants:
  43. # a) Redirect "mailto"
  44. # Requires patched lynx, currently not yet in the developent code.
  45. # Use the following two lines in the file that is configured as
  46. # RULESFILE in lynxcfg:
  47. # PermitRedirection mailto:*
  48. # Redirect mailto:* lynxcgi:/home/myhomedir/lynxcgi/mailto-form.pl?from=myname@myhost&to=*
  49. # You can also put them directly in lynx.cfg, prefixing each with
  50. # "RULE:". Replace ""myhomedir", "myname", and "myhost" with your
  51. # correct values, of course.
  52. # b) Redirect "xmailto"
  53. # Requires defining a fake proxy before starting lynx, like
  54. # export xmailto_proxy=dummy # or for csh: setenv xmailto_proxy dummy
  55. # Requires that you change "mailto" to "xmailto" each time you want
  56. # to activate a mailto link. This can be done conveniently with
  57. # a few keys: 'E', ^A, 'x', Enter.
  58. # Use the following two lines in the file that is configured as
  59. # RULESFILE in lynxcfg:
  60. # PermitRedirection xmailto:*
  61. # Redirect xmailto:* lynxcgi:/home/myhomedir/lynxcgi/mailto-form.pl?from=myname@myhost&to=*
  62. # You can also put them directly in lynx.cfg, prefixing each with
  63. # "RULE:". Replace ""myhomedir", "myname", and "myhost" with your
  64. # correct values, of course.
  65. #
  66. # Limitations:
  67. #
  68. # - Only applies to mailto URLs that appear as links or are entered at
  69. # a 'g'oto prompt. Does not apply to other ways of sending mail, like
  70. # the 'c' (COMMENT) key, mailto as a FORM action, or mailing a file
  71. # from the 'P'rinting Options screen.
  72. # - Nothing is done for charset labelling, content-transfer-encoding
  73. # of non-ASCII characters, and other MIME niceties.
  74. #
  75. # Klaus Weide 20000712
  76. ########################################################################
  77. ########## Configurable variables ######################################
  78. $SENDMAIL = '/usr/sbin/sendmail';
  79. # The location of your sendmail binary
  80. $SELFURL = 'lynxcgi:/home/lynxdev/lynxcgi/mailto-form.pl';
  81. # Where this script lives in URL space
  82. $SEND_TOKEN = '/vJhOp6eQ';
  83. # When found in the PATH_INFO part of the URL,
  84. # this causes the script to actually send mail
  85. # by calling $SENDMAIL instead of just throwing
  86. # up a form. CHANGE IT! And don't tell anyone!
  87. # Treat it like a password.
  88. # Must start with '/', probably should have only
  89. # alphanumeric ASCII characters.
  90. ## Also, make sure the first line of this script points
  91. ## to your PERL binary
  92. ########## Nothing else to change - I hope #############################
  93. ########################################################################
  94. use CGI;
  95. $|=1;
  96. ### Upcase first character
  97. ##sub ucfirst {
  98. ## s/^./\U$1/;
  99. ##}
  100. # If there are multiple occurrences of the same thing, how to join them
  101. # into one string
  102. %joiner = (from => ', ',
  103. to => ', ',
  104. cc => ', ',
  105. subject => '; ',
  106. body => "\n\n"
  107. );
  108. sub joiner {
  109. my ($key) = @_;
  110. if ($joiner{$key}) {
  111. $joiner{$key};
  112. } else {
  113. " ";
  114. }
  115. }
  116. # Here we check whether this script is called for actual sending, rather
  117. # than form generation. If so, all the rest is handled by sub sendit, below.
  118. $pathinfo = $ENV{'PATH_INFO'};
  119. if (defined($pathinfo) && $pathinfo eq $SEND_TOKEN) {
  120. $q = new CGI;
  121. print $q->header('text/plain');
  122. sendit();
  123. exit;
  124. }
  125. $method = $ENV{'REQUEST_METHOD'};
  126. $querystring = $ENV{'QUERY_STRING'};
  127. if ($querystring) {
  128. if ($method && $method eq "POST" && $ENV{'CONTENT_LENGTH'}) {
  129. $querystring =~ s/((^|\&)to=[^?&]*)\?/$1&/;
  130. $q0 = new CGI;
  131. $q = new CGI($querystring);
  132. @fields = $q0->param();
  133. foreach $key (@fields) {
  134. @vals = $q0->param($key);
  135. # print "Content-type: text/html\n\n";
  136. # print "Appending $key to \$q...\n";
  137. $q->append($key, @vals);
  138. # print "<H2>Current Values in \$q0</H2>\n";
  139. # print $q0->dump;
  140. # print "<H2>Current Values in \$q</H2>\n";
  141. # print $q->dump;
  142. }
  143. } else {
  144. $querystring =~ s/((^|\&)to=[^?&]*)\?/$1&/;
  145. $q = new CGI($querystring);
  146. }
  147. } else {
  148. $q = new CGI;
  149. }
  150. print $q->header;
  151. $long_title = $ENV{'QUERY_STRING'};
  152. $long_title =~ s/^from=([^&]*)\&to=//;
  153. $long_title = "someone" unless $long_title;
  154. $long_title = "Compose mail for $long_title";
  155. if (length($long_title) > 72) {
  156. $title = substr($long_title,0,72) . "...";
  157. } else {
  158. $title = $long_title;
  159. }
  160. $long_title =~ s/&/&amp;/g;
  161. $long_title =~ s/</&lt;/g;
  162. print
  163. $q->start_html($title), "\n",
  164. $q->h1($long_title), "\n",
  165. $q->start_form(-method=>'POST', -action => $SELFURL . $SEND_TOKEN), "\n";
  166. print "<TABLE>\n";
  167. @fields = $q->param();
  168. foreach $key (@fields) {
  169. @vals = $q->param($key);
  170. if (scalar(@vals) != 1) {
  171. print "multiple values " . scalar(@vals) ." for $key!\n";
  172. $q->param($key, join (joiner($key), @vals));
  173. }
  174. }
  175. foreach $key (@fields) {
  176. $_ = lc($key);
  177. if ($_ ne $key) {
  178. print "noncanonical case for $key!\n";
  179. $val=$q->param($key);
  180. $q->delete($key);
  181. if (!$q->param($_)) {
  182. $q->param($_, $val);
  183. } else {
  184. $q->param($_, $q->param($_) . joiner($_) . "$val");
  185. }
  186. }
  187. }
  188. foreach $key ('from', 'to', 'cc', 'subject') {
  189. print $q->Tr,
  190. $q->td(ucfirst($key) . ":"),
  191. $q->td($q->textfield(-name=>$key,
  192. -size=>60,
  193. -default=>$q->param($key))), "\n";
  194. $q->delete($key);
  195. }
  196. # Also pass on any unrecognized header fields that were specified.
  197. # This may not be a good idea for general use!
  198. # At least some dangerous header fields may have to be suppressed.
  199. @keys = $q->param();
  200. if (scalar(@keys) > (($q->param('body')) ? 1 : 0)) {
  201. print "<TR><TD colspan=2><EM>Additional headers:</EM>\n";
  202. foreach $key ($q->param()) {
  203. if ($key ne 'body') {
  204. print $q->Tr,
  205. $q->td(ucfirst($key) . ":"),
  206. $q->td($q->textfield(-name=>$key,
  207. -size=>60,
  208. -default=>$q->param($key))), "\n";
  209. }
  210. }
  211. }
  212. print "</TABLE>\n";
  213. print $q->textarea(-name=>'body',
  214. -default=>$q->param('body')), "\n";
  215. print "<PRE>\n\n</PRE>", "\n",
  216. $q->submit(-value=>"Send the message"), "\n",
  217. $q->endform, "\n";
  218. print "\n";
  219. exit;
  220. # This is for header field values.
  221. sub sanitize_field_value {
  222. my($val) = @_;
  223. $val =~ s/\0/./g;
  224. $val =~ s/\r\n/\n/g;
  225. $val =~ s/\r/\n/g;
  226. $val =~ s/\n*$//g;
  227. $val =~ s/\n+/\n/g;
  228. $val =~ s/\n(\S)/\n\t$1/g;
  229. $val;
  230. }
  231. sub sendit {
  232. open (MAIL, "| $SENDMAIL -t -oi -v") || die ("$0: Can't run sendmail: $!\n");
  233. @fields = $q->param();
  234. foreach $key (@fields) {
  235. @vals = $q->param($key);
  236. if (scalar(@vals) != 1) {
  237. print "multiple values " . scalar(@vals) ." for $key!\n";
  238. $q->param($key, join (joiner($key), @vals));
  239. }
  240. }
  241. foreach $key (@fields) {
  242. if ($key ne 'body') {
  243. if ($key =~ /[^A-Za-z0-9_-]/) {
  244. print "$0: Ignoring malformed header field named '$key'!\n";
  245. next;
  246. }
  247. print MAIL ucfirst($key) . ": " .
  248. sanitize_field_value($q->param($key)) . "\n"
  249. or die ("$0: Feeding header to sendmail failed: $!\n");
  250. }
  251. }
  252. print MAIL "\n"
  253. or die ("$0: Ending header for sendmail failed: $!\n");
  254. print MAIL $q->param('body'), "\n"
  255. or die ("$0: Feeding body to sendmail failed: $!\n");
  256. close(MAIL)
  257. or warn $! ? "Error closing pipe to sendmail: $!"
  258. : ($? & 127) ? ("Sendmail killed by signal " . ($? & 127) .
  259. ($? & 127) ? ", core dumped" : "")
  260. : "Return value " . ($? >> 8) . " from sendmail";
  261. }