+ return numerrs ? NOTOK : OK;
+}
+
+/*
+ * Add an IMAP command to the msg queue
+ */
+
+static void
+add_msg(bool queue, struct imap_msg **ret_imsg, const char *fmt, ...)
+{
+ struct imap_msg *imsg;
+ va_list ap;
+ size_t msgbufsize;
+ char *msg = NULL;
+ int rc = 63;
+
+ do {
+ msgbufsize = rc + 1;
+ msg = mh_xrealloc(msg, msgbufsize);
+ va_start(ap, fmt);
+ rc = vsnprintf(msg, msgbufsize, fmt, ap);
+ va_end(ap);
+ } while (rc >= (int) msgbufsize);
+
+ imsg = mh_xmalloc(sizeof(*imsg));
+
+ imsg->command = msg;
+ imsg->folder = NULL;
+ imsg->append = false;
+ imsg->queue = queue;
+ imsg->next = NULL;
+
+ if (msgqueue_head == NULL) {
+ msgqueue_head = imsg;
+ msgqueue_tail = imsg;
+ } else {
+ msgqueue_tail->next = imsg;
+ msgqueue_tail = imsg;
+ }
+
+ if (ret_imsg)
+ *ret_imsg = imsg;
+}
+
+/*
+ * Add an APPEND command to the queue
+ */
+
+static void
+add_append(const char *filename, const char *folder, bool queue)
+{
+ size_t filesize = rfc822size(filename);
+ struct imap_msg *imsg;
+
+ add_msg(queue, &imsg, "%s", filename);
+
+ 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
+ */
+
+static void
+imap_negotiate_tls(netsec_context *nsc)
+{
+ char *errstr;
+ struct timeval tv;
+
+ if (timestamp)
+ gettimeofday(&tv, NULL);
+
+ if (netsec_negotiate_tls(nsc, &errstr) != OK)
+ die("%s", errstr);
+
+ if (timestamp)
+ ts_report(&tv, "TLS negotation time");
+}
+
+/*
+ * Give a timestamp report.
+ */
+
+static void
+ts_report(struct timeval *tv, const char *fmt, ...)
+{
+ struct timeval now;
+ double delta;
+ va_list ap;
+
+ gettimeofday(&now, NULL);
+
+ delta = ((double) now.tv_sec) - ((double) tv->tv_sec) +
+ (now.tv_usec / 1E6 - tv->tv_usec / 1E6);
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+
+ printf(": %f sec\n", delta);
+}
+
+/*
+ * Calculate the RFC 822 size of file.
+ */
+
+static size_t
+rfc822size(const char *filename)
+{
+ FILE *f;
+ size_t total = 0, linecap = 0;
+ ssize_t rc;
+ char *line = NULL;
+
+ if (! (f = fopen(filename, "r")))
+ die("Unable to open %s: %s", filename, strerror(errno));
+
+ while ((rc = getline(&line, &linecap, f)) > 0) {
+ total += rc;
+ if (line[rc - 1] == '\n' && (rc == 1 || line[rc - 2] != '\r'))
+ total++;
+ if (line[rc - 1] != '\n')
+ total += 2;
+ }
+
+ free(line);
+
+ if (! feof(f))
+ die("Error while reading %s: %s", filename, strerror(errno));
+
+ fclose(f);
+
+ return total;