message.c
上传用户:knt0001
上传日期:2022-01-28
资源大小:264k
文件大小:14k
源码类别:

Email客户端

开发平台:

C/C++

  1. /**
  2.     eMail is a command line SMTP client.
  3.     Copyright (C) 2001 - 2008 email by Dean Jones
  4.     Software supplied and written by http://www.cleancode.org
  5.     This file is part of eMail.
  6.     eMail is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.     eMail is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.     You should have received a copy of the GNU General Public License
  15.     along with eMail; if not, write to the Free Software
  16.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17. **/
  18. #if HAVE_CONFIG_H
  19. # include "config.h"
  20. #endif
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <sys/utsname.h>
  27. #include <sys/stat.h>
  28. #include <sys/types.h>
  29. /* Autoconf manual suggests this. */
  30. #if TIME_WITH_SYS_TIME
  31. # include <sys/time.h>
  32. # include <time.h>
  33. #else
  34. # if HAVE_SYS_TIME_H
  35. #  include <sys/time.h>
  36. # else
  37. #  include <time.h>
  38. # endif
  39. #endif
  40. #include "email.h"
  41. #include "execgpg.h"
  42. #include "utils.h"
  43. #include "file_io.h"
  44. #include "addy_book.h"
  45. #include "remotesmtp.h"
  46. #include "addr_parse.h"
  47. #include "message.h"
  48. #include "mimeutils.h"
  49. #include "error.h"
  50. /**
  51.  * All functions below are pretty self explanitory.  
  52.  * They basically just preform some simple header printing 
  53.  * for our email 'message'.  I'm not doing to comment every 
  54.  * function from here because you should be able to read the 
  55.  * damn functions and get a great idea
  56. **/
  57. static void
  58. printMimeHeaders(const char *b, dstrbuf *msg, CharSetType charset)
  59. {
  60. if (Mopts.gpg_opts & GPG_ENC) {
  61. dsbPrintf(msg, "Mime-Version: 1.0rn");
  62. dsbPrintf(msg, "Content-Type: multipart/encrypted; "
  63. "protocol="application/pgp-encrypted"; " 
  64. "boundary="%s"rn", b);
  65. } else if (Mopts.gpg_opts & GPG_SIG) {
  66. dsbPrintf(msg, "Mime-Version: 1.0rn");
  67. dsbPrintf(msg, "Content-Type: multipart/signed; "
  68. "micalg=pgp-sha1; protocol="application/pgp-signature"; "
  69. "boundary="%s"rn", b);
  70. } else if (Mopts.attach) {
  71. dsbPrintf(msg, "Mime-Version: 1.0rn");
  72. dsbPrintf(msg, "Content-Type: multipart/mixed; boundary="%s"rn", b);
  73. } else {
  74. if (charset == IS_UTF8 || charset == IS_PARTIAL_UTF8) {
  75. dsbPrintf(msg, "Mime-Version: 1.0rn");
  76. dsbPrintf(msg, "Content-Type: text/plain; charset=utf-8rn");
  77. if (charset == IS_PARTIAL_UTF8) {
  78. dsbPrintf(msg, "Content-Transfer-Encoding: quoted-printablern");
  79. } else {
  80. dsbPrintf(msg, "Content-Transfer-Encoding: base64rn");
  81. }
  82. dsbPrintf(msg, "Content-Disposition: inlinern");
  83. } else if (Mopts.html) {
  84. dsbPrintf(msg, "Mime-Version: 1.0rn");
  85. dsbPrintf(msg, "Content-Type: text/htmlrn");
  86. } else {
  87. dsbPrintf(msg, "Content-Type: text/plainrn");
  88. }
  89. }
  90. }
  91. /**
  92.  * just prints the to headers, and cc headers if available
  93. **/
  94. static void
  95. printToHeaders(dlist to, dlist cc, dstrbuf *msg)
  96. {
  97. struct addr *a = (struct addr *)dlGetNext(to);
  98. dsbPrintf(msg, "To: ");
  99. while (a) {
  100. dstrbuf *tmp = formatEmailAddr(a->name, a->email);
  101. dsbPrintf(msg, "%s", tmp->str);
  102. dsbDestroy(tmp);
  103. a = (struct addr *)dlGetNext(to);
  104. if (a != NULL) {
  105. dsbPrintf(msg, ", ");
  106. } else {
  107. dsbPrintf(msg, "rn");
  108. }
  109. }
  110. if (cc != NULL) {
  111. dsbPrintf(msg, "Cc: ");
  112. a = (struct addr *)dlGetNext(cc);
  113. while (a) {
  114. dstrbuf *tmp = formatEmailAddr(a->name, a->email);
  115. dsbPrintf(msg, "%s", tmp->str);
  116. dsbDestroy(tmp);
  117. a = (struct addr *)dlGetNext(cc);
  118. if (a != NULL) {
  119. dsbPrintf(msg, ", ");
  120. } else {
  121. dsbPrintf(msg, "rn");
  122. }
  123. }
  124. }
  125. }
  126. /**
  127.  * Bcc gets a special function because it's not always printed for standard
  128.  * Mail delivery.  Only if sendmail is going to be invoked, shall it be printed
  129.  * reason being is because sendmail needs the Bcc header to know everyone who
  130.  * is going to recieve the message, when it is done with reading the Bcc headers
  131.  * it will remove this from the headers field.
  132. **/
  133. static void
  134. printBccHeaders(dlist bcc, dstrbuf *msg)
  135. {
  136. struct addr *a=NULL;
  137. if (bcc != NULL) {
  138. dsbPrintf(msg, "Bcc: ");
  139. a = (struct addr *)dlGetNext(bcc);
  140. while (a != NULL) {
  141. dstrbuf *tmp = formatEmailAddr(a->name, a->email);
  142. dsbPrintf(msg, "%s", tmp->str);
  143. dsbDestroy(tmp);
  144. a = (struct addr *)dlGetNext(bcc);
  145. if (a) {
  146. dsbPrintf(msg, ", ");
  147. } else {
  148. dsbPrintf(msg, "rn");
  149. }
  150. }
  151.         }
  152. }
  153. /** Print From Headers **/
  154. static void
  155. printFromHeaders(char *name, char *address, dstrbuf *msg)
  156. {
  157. dstrbuf *addr = formatEmailAddr(name, address);
  158. dsbPrintf(msg, "From: %srn", addr->str);
  159. dsbDestroy(addr);
  160. }
  161. /** Print Date Headers **/
  162. static void
  163. printDateHeaders(dstrbuf *msg)
  164. {
  165. time_t set_time;
  166. struct tm *lt;
  167. char buf[MAXBUF] = { 0 };
  168. set_time = time(&set_time);
  169. #ifdef USE_GMT
  170. lt = gmtime(&set_time);
  171. #else
  172. lt = localtime(&set_time);
  173. #endif
  174. #ifdef USE_GNU_STRFTIME
  175. strftime(buf, MAXBUF, "%a, %d %b %Y %H:%M:%S %z", lt);
  176. #else
  177. strftime(buf, MAXBUF, "%a, %d %b %Y %H:%M:%S %Z", lt);
  178. #endif
  179. dsbPrintf(msg, "Date: %srn", buf);
  180. }
  181. static void
  182. printExtraHeaders(dlist headers, dstrbuf *msg)
  183. {
  184. char *hdr=NULL;
  185. while ((hdr = (char *)dlGetNext(headers)) != NULL) {
  186. dsbPrintf(msg, "%srn", hdr);
  187. }
  188. }
  189. /**
  190.  * This function takes the current content that was copied
  191.  * in to us and creates a final message with the email header
  192.  * and the appended content.  It will also attach any files
  193.  * that were specified at the command line.
  194. **/
  195. static void
  196. printHeaders(const char *border, dstrbuf *msg, CharSetType msg_cs)
  197. {
  198. char *subject=Mopts.subject;
  199. char *user_name = getConfValue("MY_NAME");
  200. char *email_addr = getConfValue("MY_EMAIL");
  201. char *sm_bin = getConfValue("SENDMAIL_BIN");
  202. char *smtp_serv = getConfValue("SMTP_SERVER");
  203. char *reply_to = getConfValue("REPLY_TO");
  204. dstrbuf *dsb=NULL;
  205. if (subject) {
  206. if (Mopts.encoding) {
  207. CharSetType cs = getCharSet((u_char *)subject);
  208. if (cs == IS_UTF8) {
  209. dsb = encodeUtf8String((u_char *)subject, false);
  210. subject = dsb->str;
  211. } else if (cs == IS_PARTIAL_UTF8) {
  212. dsb = encodeUtf8String((u_char *)subject, true);
  213. subject = dsb->str;
  214. }
  215. }
  216. dsbPrintf(msg, "Subject: %srn", subject);
  217. if (dsb) {
  218. dsbDestroy(dsb);
  219. }
  220. }
  221. printFromHeaders(user_name, email_addr, msg);
  222. printToHeaders(Mopts.to, Mopts.cc, msg);
  223. /**
  224.  * We want to check here to see if we are sending mail by invoking sendmail
  225.  * If so, We want to add the BCC line to the headers.  Sendmail checks this
  226.  * Line and makes sure it sends the mail to the BCC people, and then remove
  227.  * the BCC addresses...  Keep in mind that sending to an smtp servers takes
  228.  * presidence over sending to sendmail incase both are mentioned.
  229.  */
  230. if (sm_bin && !smtp_serv) {
  231. printBccHeaders(Mopts.bcc, msg);
  232. }
  233. /* The rest of the standard headers */
  234. printDateHeaders(msg);
  235. if (reply_to) {
  236. dsbPrintf(msg, "Reply-To: <%s>rn", reply_to);
  237. }
  238. printMimeHeaders(border, msg, msg_cs);
  239. dsbPrintf(msg, "X-Mailer: Cleancode.email v%s rn", EMAIL_VERSION);
  240. if (Mopts.priority) {
  241. dsbPrintf(msg, "X-Priority: 1rn");
  242. }
  243. printExtraHeaders(Mopts.headers, msg);
  244. dsbPrintf(msg, "rn");
  245. }
  246. /**
  247.  * set up the appropriate MIME and Base64 headers for 
  248.  * the attachment of file specified in Mopts.attach
  249. **/
  250. static int
  251. attachFiles(const char *boundary, dstrbuf *out)
  252. {
  253. dstrbuf *file_name = NULL;
  254. dstrbuf *file_type = NULL;
  255. char *next_file = NULL;
  256. /*
  257. * What we will do here is parse Mopts.attach for comma delimited file
  258. * names.  If there was only one file specified with no comma, then strtok()
  259. * will just return that file and the next call to strtok() will be NULL
  260. * which will allow use to break out of our loop of attaching base64 stuff.
  261. */
  262. while ((next_file = (char *)dlGetNext(Mopts.attach)) != NULL) {
  263. FILE *current = fopen(next_file, "r");
  264. if (!current) {
  265. fatal("Could not open attachment: %s", next_file);
  266. return (ERROR);
  267. }
  268. /* If the user specified an absolute path, just get the file name */
  269. file_type = mimeFiletype(next_file);
  270. file_name = mimeFilename(next_file);
  271. /* Set our MIME headers */
  272. dsbPrintf(out, "rn--%srn", boundary);
  273. dsbPrintf(out, "Content-Transfer-Encoding: base64rn");
  274. dsbPrintf(out, "Content-Type: %s; name="%s"rn", 
  275. file_type->str, file_name->str);
  276. dsbPrintf(out, "Content-Disposition: attachment; filename="%s"rn", 
  277. file_name->str);
  278. dsbPrintf(out, "rn");
  279. /* Encode to 'out' */
  280. mimeB64EncodeFile(current, out);
  281. dsbDestroy(file_type);
  282. dsbDestroy(file_name);
  283. }
  284. return SUCCESS;
  285. }
  286. /** 
  287.  * Makes a standard plain text message while taking into
  288.  * account the MIME message types and boundary's needed
  289.  * if and when a file is attached.
  290. **/
  291. static int
  292. makeMessage(dstrbuf *in, dstrbuf *out, const char *border, CharSetType charset)
  293. {
  294. dstrbuf *enc=NULL;
  295. if (Mopts.attach) {
  296. dsbPrintf(out, "--%srn", border);
  297. if (charset == IS_UTF8 || charset == IS_PARTIAL_UTF8) {
  298. dsbPrintf(out, "Content-Type: text/plain; charset=utf-8rn");
  299. if (IS_PARTIAL_UTF8) {
  300. dsbPrintf(out, "Content-Transfer-Encoding: quoted-printablern");
  301. enc = mimeQpEncodeString((u_char *)in->str, true);
  302. } else {
  303. dsbPrintf(out, "Content-Transfer-Encoding: base64rn");
  304. enc = mimeB64EncodeString((u_char *)in->str, in->len, true);
  305. }
  306. dsbPrintf(out, "Content-Disposition: inlinernrn");
  307. } else if (Mopts.html) {
  308. dsbPrintf(out, "Content-Type: text/htmlrnrn");
  309. enc = DSB_NEW;
  310. dsbCat(enc, in->str);
  311. } else {
  312. dsbPrintf(out, "Content-Type: text/plainrnrn");
  313. enc = DSB_NEW;
  314. dsbCat(enc, in->str);
  315. }
  316. } else {
  317. if (charset == IS_UTF8) {
  318. enc = mimeB64EncodeString((u_char *)in->str, in->len, true);
  319. } else if (charset == IS_PARTIAL_UTF8) {
  320. enc = mimeQpEncodeString((u_char *)in->str, true);
  321. } else {
  322. enc = DSB_NEW;
  323. dsbCat(enc, in->str);
  324. }
  325. }
  326. dsbPrintf(out, "%srn", enc->str);
  327. if (Mopts.attach) {
  328. if (attachFiles(border, out) == ERROR) {
  329. return ERROR;
  330. }
  331. dsbPrintf(out, "rnrn--%s--rn", border);
  332. }
  333. dsbDestroy(enc);
  334. return 0;
  335. }
  336. /**
  337.  * Makes a message type specifically for gpg encryption and 
  338.  * signing.  Specific MIME message descriptions are needed
  339.  * when signing/encrypting a file before it is actuall signed
  340.  * or encrypted.  This function takes care of that.
  341. **/
  342. static int
  343. makeGpgMessage(dstrbuf *in, dstrbuf *out, const char *border)
  344. {
  345. dstrbuf *qp=NULL;
  346. assert(in != NULL);
  347. assert(out != NULL);
  348. assert(border != NULL);
  349. if (Mopts.attach) {
  350. dsbPrintf(out, "Content-Type: multipart/mixed; "
  351. "boundary="%s"rnrn", border);
  352. dsbPrintf(out, "rn--%srn", border);
  353. }
  354. if (Mopts.html) {
  355. dsbPrintf(out, "Content-Type: text/htmlrn");
  356. } else {
  357. dsbPrintf(out, "Content-Type: text/plainrn");
  358. }
  359. dsbPrintf(out, "Content-Transfer-Encoding: quoted-printablernrn");
  360. qp = mimeQpEncodeString((u_char *)in->str, true);
  361. dsbnCat(out, qp->str, qp->len);
  362. dsbDestroy(qp);
  363. if (Mopts.attach) {
  364. attachFiles(border, out);
  365. dsbPrintf(out, "rn--%s--rn", border);
  366. }
  367. return 0;
  368. }
  369. /**
  370.  * Creates a signed message with gpg and takes into 
  371.  * account the correct MIME message types to add to 
  372.  * the message.
  373. **/
  374. static dstrbuf *
  375. createGpgEmail(dstrbuf *msg, GpgCallType gpg_type)
  376. {
  377. dstrbuf *tmpbuf=DSB_NEW;
  378. dstrbuf *gpgdata=NULL, *buf=DSB_NEW;
  379. dstrbuf *border1=NULL, *border2=NULL;
  380. assert(msg != NULL);
  381. /* Create two borders if we're attaching files */
  382. border1 = mimeMakeBoundary();
  383. if (Mopts.attach) {
  384. border2 = mimeMakeBoundary();
  385. } else {
  386. border2 = DSB_NEW;
  387. }
  388. if (makeGpgMessage(msg, tmpbuf, border2->str) < 0) {
  389. dsbDestroy(buf);
  390. buf=NULL;
  391. goto end;
  392. }
  393. gpgdata = callGpg(tmpbuf, gpg_type);
  394. if (!gpgdata) {
  395. dsbDestroy(buf);
  396. buf=NULL;
  397. goto end;
  398. }
  399. printHeaders(border1->str, buf, IS_ASCII);
  400. dsbPrintf(buf, "rn--%srn", border1->str);
  401. if (gpg_type & GPG_ENC) {
  402. dsbPrintf(buf, "Content-Type: application/pgp-encryptedrnrn");
  403. dsbPrintf(buf, "Version: 1rn");
  404. dsbPrintf(buf, "rn--%srn", border1->str);
  405. dsbPrintf(buf, "Content-type: application/octet-stream; "
  406.        "name=encrypted.ascrnrn");
  407. } else if (gpg_type & GPG_SIG) {
  408. dsbPrintf(buf, "%srn", tmpbuf->str);
  409. dsbPrintf(buf, "rn--%srn", border1->str);
  410. dsbPrintf(buf, "Content-Type: application/pgp-signaturern");
  411. dsbPrintf(buf, "Content-Description: This is a digitally signed messagernrn");
  412. dsbPrintf(buf, "%s", gpgdata->str);
  413. dsbPrintf(buf, "rn--%s--rn", border1->str);
  414. end:
  415. dsbDestroy(tmpbuf);
  416. dsbDestroy(gpgdata);
  417. dsbDestroy(border1);
  418. dsbDestroy(border2);
  419. return buf;
  420. }
  421. /**
  422.  * Creates a plain text (or html) email and 
  423.  * specifies the necessary MIME types if needed
  424.  * due to attaching base64 files.
  425.  * when this function is done, it will rewind
  426.  * the file position and return an open file
  427. **/
  428. static dstrbuf *
  429. createPlainEmail(dstrbuf *msg) 
  430. {
  431. dstrbuf *border=NULL;
  432. dstrbuf *buf=DSB_NEW;
  433. CharSetType cs;
  434. if (Mopts.attach) {
  435. border = mimeMakeBoundary();
  436. } else {
  437. border = DSB_NEW;
  438. }
  439. if (Mopts.encoding) {
  440. cs = getCharSet((u_char *)msg->str);
  441. } else {
  442. cs = IS_ASCII;
  443. }
  444. printHeaders(border->str, buf, cs);
  445. if (makeMessage(msg, buf, border->str, cs) < 0) {
  446. dsbDestroy(buf);
  447. buf=NULL;
  448. }
  449. dsbDestroy(border);
  450. return buf;
  451. }
  452. /**
  453.  * this is the function that takes over from main().  
  454.  * It will call all functions nessicary to finish off the 
  455.  * rest of the program and then return properly. 
  456. **/
  457. void
  458. createMail(void)
  459. {
  460. dstrbuf *msg=NULL;
  461. char subject[MAXBUF]={0};
  462. /**
  463.  * first let's check if someone has tried to send stuff in from STDIN 
  464.  * if they have, let's call a read to stdin
  465.  */
  466. if (isatty(STDIN_FILENO) == 0) {
  467. msg = readInput();
  468. if (!msg) {
  469. fatal("Problem reading from STDIN redirectn");
  470. properExit(ERROR);
  471. }
  472. } else {
  473. /* If they aren't sending a blank email */
  474. if (!Mopts.blank) {
  475. /* let's check if they want to add a subject or not */
  476. if (Mopts.subject == NULL) {
  477. fprintf(stderr, "Subject: ");
  478. fgets(subject, sizeof(subject)-1, stdin);
  479. chomp(subject);
  480. Mopts.subject = subject;
  481. }
  482. /* Now we need to let them create a file */
  483. msg = editEmail();
  484. if (!msg) {
  485. properExit(ERROR);
  486. }
  487. } else {
  488. /* Create a blank message */
  489. msg = DSB_NEW;
  490. }
  491. }
  492. /* Create a message according to the type */
  493. if (Mopts.gpg_opts) {
  494. global_msg = createGpgEmail(msg, Mopts.gpg_opts);
  495. } else {
  496. global_msg = createPlainEmail(msg);
  497. }
  498. if (!global_msg) {
  499. dsbDestroy(msg);
  500. properExit(ERROR);
  501. }
  502. dsbDestroy(msg);
  503. sendmail(global_msg);
  504. }