X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/fd65918863a741ab0d2a0749601915ea612e23d7..9f1e781a5c2beb9df943c832300a3f5bee1e4ed3:/uip/imaptest.c diff --git a/uip/imaptest.c b/uip/imaptest.c index ff83326f..e624bc4d 100644 --- a/uip/imaptest.c +++ b/uip/imaptest.c @@ -5,9 +5,10 @@ * complete copyright information. */ -#include -#include -#include +#include "h/mh.h" +#include "sbr/error.h" +#include "h/utils.h" +#include "h/netsec.h" #include #include #include "h/done.h" @@ -30,8 +31,10 @@ X("noqueue", 0, NOQUEUESW) \ X("append filename", 0, APPENDSW) \ X("afolder foldername", 0, AFOLDERSW) \ + X("batch filename", 0, BATCHSW) \ X("timestamp", 0, TIMESTAMPSW) \ X("notimestamp", 0, NOTIMESTAMPSW) \ + X("timeout", 0, TIMEOUTSW) \ X("version", 0, VERSIONSW) \ X("help", 0, HELPSW) \ @@ -47,7 +50,7 @@ struct imap_msg; struct imap_msg { char *command; /* Command to send */ - const char *folder; /* Folder (for append) */ + char *folder; /* Folder (for append) */ bool queue; /* If true, queue for later delivery */ bool append; /* If true, append "command" to mbox */ size_t msgsize; /* RFC822 size of message */ @@ -92,7 +95,9 @@ static void imap_negotiate_tls(netsec_context *); static void add_msg(bool queue, struct imap_msg **, const char *fmt, ...) CHECK_PRINTF(3, 4); -static void add_append(const char *filename, const char *folder); +static void add_append(const char *filename, const char *folder, bool queue); + +static void batchfile(const char *filename, char *afolder, bool queue); static size_t rfc822size(const char *filename); @@ -103,7 +108,7 @@ main (int argc, char **argv) { bool sasl = false, tls = false, initialtls = false; bool snoop = false, queue = false; - int fd; + int fd, timeout = 0; char *saslmech = NULL, *host = NULL, *port = "143", *user = NULL; char *cp, **argp, buf[BUFSIZ], *oauth_svc = NULL, *errstr, **arguments, *p; char *afolder = NULL; @@ -154,10 +159,23 @@ main (int argc, char **argv) continue; case APPENDSW: if (!*argp || (**argp == '-')) - die("missing argument to %s", argp[-2]); + die("missing argument to %s", argp[-1]); if (! afolder) die("Append folder must be set with -afolder first"); - add_append(*argp++, afolder); + add_append(*argp++, afolder, queue); + continue; + + case BATCHSW: + if (! *argp || (**argp == '-')) + die("missing argument to %s", argp[-1]); + batchfile(*argp++, afolder, queue); + continue; + + case TIMEOUTSW: + if (! *argp || (**argp == '-')) + die("missing argument to %s", argp[-1]); + if (! (timeout = atoi(*argp++))) + die("Invalid timeout: %s", argp[-1]); continue; case SNOOPSW: @@ -208,7 +226,7 @@ main (int argc, char **argv) } else if (*cp == '+') { if (*(cp + 1) == '\0') die("Invalid null folder name"); - add_msg(0, NULL, "SELECT \"%s\"", cp + 1); + add_msg(false, NULL, "SELECT \"%s\"", cp + 1); } else { add_msg(queue, NULL, "%s", cp); } @@ -224,6 +242,9 @@ main (int argc, char **argv) netsec_set_hostname(nsc, host); + if (timeout) + netsec_set_timeout(nsc, timeout); + if (snoop) netsec_set_snoop(nsc, 1); @@ -297,7 +318,7 @@ main (int argc, char **argv) goto finish; } - if (get_imap_response(nsc, "CAPABILITY", &capstring, NULL, false, true, + if (get_imap_response(nsc, "CAPABILITY ", &capstring, NULL, false, true, &errstr) != OK) { fprintf(stderr, "Cannot get CAPABILITY response: %s\n", errstr); goto finish; @@ -308,7 +329,9 @@ main (int argc, char **argv) goto finish; } - parse_capability(capstring, strlen(capstring)); + p = capstring + 11; /* "CAPABILITY " */ + + parse_capability(p, strlen(p)); free(capstring); } @@ -363,7 +386,7 @@ main (int argc, char **argv) goto finish; } - if (get_imap_response(nsc, "CAPABILITY", &capstring, NULL, false, + if (get_imap_response(nsc, "CAPABILITY ", &capstring, NULL, false, true, &errstr) != OK) { fprintf(stderr, "Cannot get CAPABILITY response: %s\n", errstr); goto finish; @@ -374,7 +397,9 @@ main (int argc, char **argv) goto finish; } - parse_capability(capstring, strlen(capstring)); + p = capstring + 11; /* "CAPABILITY " */ + + parse_capability(p, strlen(p)); free(capstring); } @@ -413,6 +438,7 @@ main (int argc, char **argv) msgqueue_head = imsg->next; free(imsg->command); + free(imsg->folder); free(imsg); } @@ -527,7 +553,7 @@ imap_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, unsigned int *outdatalen, void *context, char **errstr) { int rc, snoopoffset; - char *mech, *line; + char *mech, *line, *capstring, *p; size_t len; netsec_context *nsc = (netsec_context *) context; @@ -655,16 +681,24 @@ imap_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, case NETSEC_SASL_FINISH: line = NULL; - if (get_imap_response(nsc, NULL, NULL, &line, false, true, - errstr) != OK) + if (get_imap_response(nsc, "CAPABILITY ", &capstring, &line, + false, true, errstr) != OK) return NOTOK; /* * We MIGHT get a capability response here. If so, be sure we - * parse it. + * parse it. We ALSO might get a untagged CAPABILITY response. + * Which one should we prefer? I guess we'll go with the untagged + * one. */ - if (line && has_prefix(line, "OK [CAPABILITY ")) { - char *p = line + 15, *q; + if (capstring) { + p = capstring + 11; /* "CAPABILITY " */ + parse_capability(p, strlen(p)); + free(capstring); + } else if (line && has_prefix(line, "OK [CAPABILITY ")) { + char *q; + + p = line + 15; q = strchr(p, ']'); if (q) @@ -757,7 +791,7 @@ send_append(netsec_context *nsc, struct imap_msg *imsg, char **errstr) FILE *f; bool nonsynlit = false; char *status = NULL, *line = NULL; - size_t linesize = 0, total = 0; + size_t linesize = 0; ssize_t rc; /* @@ -769,17 +803,6 @@ send_append(netsec_context *nsc, struct imap_msg *imsg, char **errstr) if (capability_set("LITERAL+") || (capability_set("LITERAL-") && imsg->msgsize <= 4096)) { nonsynlit = true; - } else { - /* - * Since we need to do a command contination, make sure we have - * no outstanding commands. - */ - - if (netsec_flush(nsc, errstr) != OK) - return NOTOK; - if (get_imap_response(nsc, NULL, NULL, NULL, false, - false, errstr) != OK) - return NOTOK; } /* @@ -808,6 +831,7 @@ send_append(netsec_context *nsc, struct imap_msg *imsg, char **errstr) free(status); return NOTOK; } + free(status); } /* @@ -824,25 +848,22 @@ send_append(netsec_context *nsc, struct imap_msg *imsg, char **errstr) if (rc > 1 && line[rc - 1] == '\n' && line[rc - 2] == '\r') { if (netsec_write(nsc, line, rc, errstr) != OK) { free(line); + fclose(f); return NOTOK; } - fprintf(stderr, "Wrote %u bytes\n", (unsigned int) rc); - total += rc; } else { if (line[rc - 1] == '\n') rc--; if (netsec_write(nsc, line, rc, errstr) != OK) { free(line); + fclose(f); return NOTOK; } if (netsec_write(nsc, "\r\n", 2, errstr) != OK) { free(line); + fclose(f); return NOTOK; } - - fprintf(stderr, "Write %u bytes (plus 2 for CR-LF)\n", - (unsigned int) rc); - total += rc + 2; } } @@ -851,10 +872,11 @@ send_append(netsec_context *nsc, struct imap_msg *imsg, char **errstr) if (! feof(f)) { netsec_err(errstr, "Error reading %s: %s", imsg->command, strerror(errno)); + fclose(f); return NOTOK; } - fprintf(stderr, "%u bytes written total\n", (unsigned int) total); + fclose(f); /* * Send a final \r\n for the end of the command @@ -901,7 +923,7 @@ getline: } /* * Special case; return now but don't dequeue the tag, - * since we will want to get final result later. + * since we will want to get final result later. */ return OK; } else { @@ -923,6 +945,9 @@ getline: numerrs = true; netsec_err(errstr, "%s", line + strlen(cmd2->tag)); } + if (timestamp) + ts_report(&cmd2->start, "Command (%s) execution " + "time", cmd2->prefix); free(cmd2); if (status) *status = getcpy(line); @@ -982,18 +1007,99 @@ add_msg(bool queue, struct imap_msg **ret_imsg, const char *fmt, ...) */ static void -add_append(const char *filename, const char *folder) +add_append(const char *filename, const char *folder, bool queue) { size_t filesize = rfc822size(filename); struct imap_msg *imsg; - add_msg(false, &imsg, "%s", filename); + add_msg(queue, &imsg, "%s", filename); - imsg->folder = folder; + imsg->folder = getcpy(folder); imsg->append = true; imsg->msgsize = filesize; } +/* + * Process a batch file, which can contain commands (and some arguments) + */ + +static void +batchfile(const char *filename, char *afolder, bool queue) +{ + FILE *f; + char *line = NULL; + size_t linesize = 0; + ssize_t rc; + bool afolder_alloc = false; + + if (!(f = fopen(filename, "r"))) { + die("Unable to open batch file %s: %s", filename, strerror(errno)); + } + + while ((rc = getline(&line, &linesize, f)) > 0) { + line[rc - 1] = '\0'; + if (*line == '-') { + switch (smatch (line + 1, switches)) { + case QUEUESW: + queue = true; + continue; + case NOQUEUESW: + queue = false; + continue; + case AFOLDERSW: + if (afolder_alloc) + free(afolder); + rc = getline(&line, &linesize, f); + if (rc <= 0) + die("Unable to read next line for -afolder"); + if (rc == 1) + die("Folder name cannot be blank"); + line[rc - 1] = '\0'; + afolder = getcpy(line); + afolder_alloc = true; + continue; + + case APPENDSW: + rc = getline(&line, &linesize, f); + if (rc <= 0) + die("Unable to read filename for -append"); + if (rc == 1) + die("Filename for -append cannot be blank"); + line[rc - 1] = '\0'; + add_append(line, afolder, queue); + continue; + + case AMBIGSW: + ambigsw (line, switches); + done (1); + case UNKWNSW: + die("%s unknown", line); + default: + die("Switch %s not supported in batch mode", line); + } + } else if (*line == '+') { + if (*(line + 1) == '\0') + die("Invalid null folder name"); + add_msg(false, NULL, "SELECT \"%s\"", line + 1); + } else { + if (*line == '\0') + continue; /* Ignore blank line */ + add_msg(queue, NULL, "%s", line); + } + } + + if (!feof(f)) { + die("Read of \"%s\" failed: %s", filename, strerror(errno)); + } + + fclose(f); + + if (afolder_alloc) + free(afolder); + + free(line); +} + /* * Negotiate TLS connection, with optional timestamp */