mail: Detect terminal input, end of file, better argument parsing.

* If the input is a terminal rather than a file or pipe, only then look
  for the single period on a line as end of text.  Also look for end of
  file as an end of the text, so that piped in text works.
* Parse multiple e-mail addresses properly, adding a comma between them
  (a space doesn't work).  Also allow mixing of "to" e-mail addresses
  and command line switches, previously all "to" addresses had to be
  at the end.
* Fewer blank lines in the output, make it look nicer, remove things
  like a redundant display of the body text before text was read.  Also
  no output text when just piping in a message.
* Avoid buffer overrun by using fgets instead of gets.
* Use stderr for text the user likely doesn't want to save, and for
  prompts that would be invisible if stdout was redirected to a file.

Signed-off-by: Augustin Cavalier <[email protected]>
This commit is contained in:
Alexander G. M. Smith
2016-03-02 22:11:15 -05:00
committed by Augustin Cavalier
parent f1b2a12f3b
commit f765016ffd
+45 -36
View File
@@ -6,17 +6,18 @@
* Santiago (Jacques) Lema * Santiago (Jacques) Lema
* Jérôme Duval, [email protected] * Jérôme Duval, [email protected]
* Augustin Cavalier, <waddlesplash> * Augustin Cavalier, <waddlesplash>
* Alexander G. M. Smith <[email protected]>
*/ */
#include <Application.h>
#include <String.h>
#include <E-mail.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <Application.h>
#include <E-mail.h>
#include <String.h>
#define APP_SIG "application/x-vnd.Haiku-mail_utils-mail" #define APP_SIG "application/x-vnd.Haiku-mail_utils-mail"
int main(int argc, char* argv[]) int main(int argc, char* argv[])
@@ -28,17 +29,15 @@ int main(int argc, char* argv[])
fprintf(stdout,"This program can only send mail, not read it.\n"); fprintf(stdout,"This program can only send mail, not read it.\n");
fprintf(stdout,"usage: %s [-v] [-s subject] [-c cc-addr] " fprintf(stdout,"usage: %s [-v] [-s subject] [-c cc-addr] "
"[-b bcc-addr] to-addr ...\n", argv[0]); "[-b bcc-addr] to-addr ...\n", argv[0]);
fflush(stdout);
return 0; return 0;
} }
char *subject = "No title"; char *subject = "No subject";
char *cc = ""; char *cc = "";
char *bcc = ""; char *bcc = "";
BString to = ""; BString to;
BString body = ""; bool verbose = false;
bool verbose =false;
// Parse arguments // Parse arguments
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) if (strcmp(argv[i], "-v") == 0)
@@ -53,51 +52,60 @@ int main(int argc, char* argv[])
bcc = argv[i+1]; bcc = argv[i+1];
i++; i++;
} else { } else {
if (to.Length() > 0)
to.Append(", ");
to.Append(argv[i]); to.Append(argv[i]);
if (i < argc - 1) }
to.Append(" ");
}
} }
if (verbose) { if (verbose) {
fprintf(stdout, "\n"); fprintf(stdout, "\n");
fprintf(stdout, "To:\t<%s> \n", to.String()); fprintf(stdout, "To:\t%s\n", to.String());
fprintf(stdout, "Cc:\t<%s> \n", cc); fprintf(stdout, "Cc:\t%s\n", cc);
fprintf(stdout, "Bcc:\t<%s> \n", bcc); fprintf(stdout, "Bcc:\t%s\n", bcc);
fprintf(stdout, "Subj:\t<%s> \n", subject); fprintf(stdout, "Subj:\t%s\n", subject);
fprintf(stdout, "Body:\t<%s> \n", body.String()); fprintf(stdout, "\n");
fprintf(stdout, "\n");
} }
// Check if recipients are valid // Check if recipients are valid
if (strcmp(to.String(), "") == 0 && if (strcmp(to.String(), "") == 0 &&
strcmp(cc, "") == 0 && strcmp(cc, "") == 0 &&
strcmp(bcc, "") == 0) { strcmp(bcc, "") == 0) {
fprintf(stdout, "[Error]: You must specify at least one recipient " fprintf(stderr, "[Error]: You must specify at least one recipient "
"in to, cc or bcc fields.\n"); "in to, cc or bcc fields.\n");
return -1; return -1;
} }
// Read each line until we get a single dot "." on a line bool isTerminal = isatty(STDIN_FILENO) != 0;
if (isTerminal) {
fprintf(stderr, "Now type your message.\n"
"Type '.' alone on a line to end your text and send it.\n");
}
BString body;
char line[32768] = ""; char line[32768] = "";
printf("Now type your message.\nType '.' alone on a line to send it.\n"); // Read each line and collect the body text until we get an end of text
// marker. That's a single dot "." on a line typed in by the user,
// or end of file when reading a file.
do { do {
gets(line); if (fgets(line, sizeof(line), stdin) == NULL) {
// End of file or an error happened, just send collected body text.
if (strcmp(line, ".") != 0) { break;
body.Append(line).Append("\n");
} }
// fprintf(stdout,"Line: %s \n",line);
} while (strcmp(line, ".") != 0);
if (isTerminal && strcmp(line, ".\n") == 0)
break;
body.Append(line);
} while (true);
if (verbose) if (verbose)
fprintf(stdout, "\nBody:\n%s\n", body.String()); fprintf(stdout, "\nBody:\n%s\n", body.String());
if (verbose) if (verbose)
fprintf(stdout, "\nSending E-mail...\n"); fprintf(stderr, "Sending E-mail...\n");
fflush(stdout); fflush(stdout);
BMailMessage mail; BMailMessage mail;
@@ -105,14 +113,15 @@ int main(int argc, char* argv[])
mail.AddHeaderField(B_MAIL_CC, cc); mail.AddHeaderField(B_MAIL_CC, cc);
mail.AddHeaderField(B_MAIL_BCC, bcc); mail.AddHeaderField(B_MAIL_BCC, bcc);
mail.AddHeaderField(B_MAIL_SUBJECT, subject); mail.AddHeaderField(B_MAIL_SUBJECT, subject);
mail.AddContent(body.String(), strlen(body.String())); mail.AddContent(body.String(), body.Length());
status_t result = mail.Send(); status_t result = mail.Send();
if (result == B_OK) { if (result == B_OK) {
fprintf(stdout, "\nMessage was sent successfully.\n"); if (verbose)
fprintf(stderr, "Message was sent successfully.\n");
return 0; return 0;
} }
fprintf(stdout, "Message failed to send: %s", strerror(result)); fprintf(stderr, "Message failed to send: %s\n", strerror(result));
return result; return result;
} }