]> diplodocus.org Git - nmh/blobdiff - uip/imaptest.c
SPECS: Use `command-line interface' in one-line summary.
[nmh] / uip / imaptest.c
index c4919f7b00a8e3f98f5cee1e44d13c8a09a4fbe3..2032e671a9f039be78109041048c564a336fcd17 100644 (file)
@@ -9,6 +9,7 @@
 #include <h/utils.h>
 #include <h/netsec.h>
 #include <stdarg.h>
+#include <sys/time.h>
 #include "h/done.h"
 #include "sbr/base64.h"
 
@@ -39,14 +40,27 @@ DEFINE_SWITCH_ENUM(IMAPTEST);
 DEFINE_SWITCH_ARRAY(IMAPTEST, switches);
 #undef X
 
+struct imap_msg;
+
+struct imap_msg {
+    char *command;             /* Command to send */
+    int queue;                 /* If true, queue for later delivery */
+    struct imap_msg *next;     /* Next pointer */
+};
+
+struct imap_msg *msgqueue_head = NULL;
+struct imap_msg *msgqueue_tail = NULL;
+
 struct imap_cmd;
 
 struct imap_cmd {
     char tag[16];              /* Command tag */
+    struct timeval start;      /* Time command was sent */
     struct imap_cmd *next;     /* Next pointer */
 };
 
 static struct imap_cmd *cmdqueue = NULL;
+
 static char *saslmechs = NULL;
 svector_t imap_capabilities = NULL;
 
@@ -59,21 +73,29 @@ static int capability_set(const char *);
 static void clear_capability(void);
 static int have_capability(void);
 static int send_imap_command(netsec_context *, int noflush, char **errstr,
-                            const char *fmt, ...);
+                            const char *fmt, ...) CHECK_PRINTF(4, 5);
 static int get_imap_response(netsec_context *, const char *token,
                             char **tokenresp, char **status, int failerr,
                             char **errstr);
 
+static void ts_report(const char *str, struct timeval *tv);
+
+static void add_msg(int queue, const char *fmt, ...) CHECK_PRINTF(2, 3);
+
+static bool timestamp = false;
+
 int
 main (int argc, char **argv)
 {
-    bool timestamp = false, sasl = false, tls = false, initialtls = false;
+    bool sasl = false, tls = false, initialtls = false;
     bool snoop = false;
     int fd;
     char *saslmech = NULL, *host = NULL, *port = "143", *user = NULL;
     char *cp, **argp, buf[BUFSIZ], *oauth_svc = NULL, *errstr, **arguments, *p;
     netsec_context *nsc = NULL;
+    struct imap_msg *imsg;
     size_t len;
+    struct timeval tv_start, tv_connect, tv_auth;
 
     if (nmh_init(argv[0], 1)) { return 1; }
 
@@ -150,6 +172,12 @@ main (int argc, char **argv)
                timestamp = false;
                continue;
            }
+       } else if (*cp == '+') {
+           if (*(cp + 1) == '\0')
+               adios(NULL, "Invalid null folder name");
+           add_msg(0, "SELECT \"%s\"", cp + 1);
+       } else {
+           add_msg(0, "%s", cp);
        }
     }
 
@@ -172,28 +200,36 @@ main (int argc, char **argv)
        }
     }
 
+    if (timestamp)
+       gettimeofday(&tv_start, NULL);
+
     if ((fd = client(host, port, buf, sizeof(buf), snoop)) == NOTOK)
        adios(NULL, "Connect failed: %s", buf);
 
+    if (timestamp) {
+       ts_report("Connect time", &tv_start);
+       gettimeofday(&tv_connect, NULL);
+    }
+
     netsec_set_fd(nsc, fd, fd);
     netsec_set_snoop(nsc, snoop);
 
     if (initialtls || tls) {
        if (netsec_set_tls(nsc, 1, 0, &errstr) != OK)
-           adios(NULL, errstr);
+           adios(NULL, "%s", errstr);
 
        if (initialtls && netsec_negotiate_tls(nsc, &errstr) != OK)
-           adios(NULL, errstr);
+           adios(NULL, "%s", errstr);
     }
 
     if (sasl) {
        if (netsec_set_sasl_params(nsc, "imap", saslmech, imap_sasl_callback,
                                   nsc, &errstr) != OK)
-           adios(NULL, errstr);
+           adios(NULL, "%s", errstr);
     }
 
     if ((cp = netsec_readline(nsc, &len, &errstr)) == NULL) {
-       adios(NULL, errstr);
+       adios(NULL, "%s", errstr);
     }
 
     if (has_prefix(cp, "* BYE")) {
@@ -259,7 +295,7 @@ main (int argc, char **argv)
            goto finish;
        }
        if (netsec_negotiate_tls(nsc, &errstr) != OK) {
-           adios(NULL, errstr);
+           adios(NULL, "%s", errstr);
        }
     }
 
@@ -286,12 +322,69 @@ main (int argc, char **argv)
        }
     }
 
+    if (!have_capability()) {
+        char *capstring;
+
+       if (send_imap_command(nsc, 0, &errstr, "CAPABILITY") != OK) {
+           fprintf(stderr, "Unable to send CAPABILITY command: %s\n", errstr);
+           goto finish;
+       }
+
+       if (get_imap_response(nsc, "CAPABILITY", &capstring, NULL, 1,
+                             &errstr) != OK) {
+           fprintf(stderr, "Cannot get CAPABILITY response: %s\n", errstr);
+           goto finish;
+       }
+
+       if (! capstring) {
+           fprintf(stderr, "No CAPABILITY response seen\n");
+           goto finish;
+       }
+
+       parse_capability(capstring, strlen(capstring));
+       free(capstring);
+    }
+
+    if (timestamp) {
+       ts_report("Authentication time", &tv_connect);
+       gettimeofday(&tv_auth, NULL);
+    }
+
+    while (msgqueue_head != NULL) {
+       imsg = msgqueue_head;
+
+       if (send_imap_command(nsc, imsg->queue, &errstr, "%s",
+                             imsg->command) != OK) {
+           fprintf(stderr, "Cannot send command \"%s\": %s\n", imsg->command,
+                   errstr);
+           free(errstr);
+           goto finish;
+       }
+
+       if (! imsg->queue) {
+           if (get_imap_response(nsc, NULL, NULL, NULL, 0, &errstr) != OK) {
+               fprintf(stderr, "Unable to get response for command "
+                       "\"%s\": %s\n", imsg->command, errstr);
+               goto finish;
+           }
+       }
+
+       msgqueue_head = imsg->next;
+
+       free(imsg->command);
+       free(imsg);
+    }
+
+    ts_report("Total command execution time", &tv_auth);
+
     send_imap_command(nsc, 0, NULL, "LOGOUT");
     get_imap_response(nsc, NULL, NULL, NULL, 0, NULL);
 
 finish:
     netsec_shutdown(nsc);
 
+    ts_report("Total elapsed time", &tv_start);
+
     exit(0);
 }
 
@@ -324,7 +417,7 @@ parse_capability(const char *cap, unsigned int len)
     }
 
     for (i = 0; caplist[i] != NULL; i++) {
-       if (has_prefix(caplist[i], "AUTH=") && caplist[i] + 5 != '\0') {
+       if (has_prefix(caplist[i], "AUTH=") && *(caplist[i] + 5) != '\0') {
            if (saslmechs) {
                saslmechs = add(" ", saslmechs);
            }
@@ -334,7 +427,6 @@ parse_capability(const char *cap, unsigned int len)
        }
     }
 
-    free(caplist);
     free(str);
 }
 
@@ -505,6 +597,7 @@ 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, 1, errstr) != OK)
            return NOTOK;
        /*
@@ -512,7 +605,7 @@ imap_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
         * parse it.
         */
 
-       if (has_prefix(line, "OK [CAPABILITY ")) {
+       if (line && has_prefix(line, "OK [CAPABILITY ")) {
            char *p = line + 15, *q;
            q = strchr(p, ']');
 
@@ -554,6 +647,9 @@ send_imap_command(netsec_context *nsc, int noflush, char **errstr,
 
     snprintf(cmd->tag, sizeof(cmd->tag), "A%u ", seq++);
 
+    if (timestamp)
+       gettimeofday(&cmd->start, NULL);
+
     if (netsec_write(nsc, cmd->tag, strlen(cmd->tag), errstr) != OK) {
        free(cmd);
        return NOTOK;
@@ -612,6 +708,8 @@ getline:
 
            if (has_prefix(line, cmdqueue->tag)) {
                cmd = cmdqueue;
+               if (timestamp)
+                   ts_report("Command execution time", &cmd->start);
                cmdqueue = cmd->next;
                free(cmd);
            } else {
@@ -639,3 +737,57 @@ getline:
 
     return numerrs == 0 ? OK : NOTOK;
 }
+
+/*
+ * Add an IMAP command to the msg queue
+ */
+
+static void
+add_msg(int queue, 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->queue = queue;
+    imsg->next = NULL;
+
+    if (msgqueue_head == NULL) {
+       msgqueue_head = imsg;
+       msgqueue_tail = imsg;
+    } else {
+       msgqueue_tail->next = imsg;
+       msgqueue_tail = imsg;
+    }
+}
+
+/*
+ * Give a timestamp report.
+ */
+
+static void
+ts_report(const char *str, struct timeval *tv)
+{
+    struct timeval now;
+    double delta;
+
+    gettimeofday(&now, NULL);
+
+    delta = ((double) now.tv_sec) - ((double) tv->tv_sec) +
+           (now.tv_usec / 1E6 - tv->tv_usec / 1E6);
+
+    printf("%s: %f sec\n", str, delta);
+}