1 /* imaptest.c -- A program to send commands to an IMAP server
3 * This code is Copyright (c) 2017, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
9 #include "sbr/getarguments.h"
10 #include "sbr/smatch.h"
11 #include "sbr/client.h"
12 #include "sbr/getcpy.h"
13 #include "sbr/brkstring.h"
14 #include "sbr/ambigsw.h"
15 #include "sbr/print_version.h"
16 #include "sbr/print_help.h"
17 #include "sbr/error.h"
23 #include "sbr/base64.h"
25 #define IMAPTEST_SWITCHES \
26 X("host hostname", 0, HOSTSW) \
27 X("user username", 0, USERSW) \
28 X("port name/number", 0, PORTSW) \
29 X("snoop", 0, SNOOPSW) \
30 X("nosnoop", 0, NOSNOOPSW) \
31 X("sasl", 0, SASLSW) \
32 X("nosasl", 0, NOSASLSW) \
33 X("saslmech", 0, SASLMECHSW) \
34 X("authservice", 0, AUTHSERVICESW) \
36 X("notls", 0, NOTLSSW) \
37 X("initialtls", 0, INITIALTLSSW) \
38 X("queue", 0, QUEUESW) \
39 X("noqueue", 0, NOQUEUESW) \
40 X("append filename", 0, APPENDSW) \
41 X("afolder foldername", 0, AFOLDERSW) \
42 X("batch filename", 0, BATCHSW) \
43 X("timestamp", 0, TIMESTAMPSW) \
44 X("notimestamp", 0, NOTIMESTAMPSW) \
45 X("timeout", 0, TIMEOUTSW) \
46 X("version", 0, VERSIONSW) \
47 X("help", 0, HELPSW) \
49 #define X(sw, minchars, id) id,
50 DEFINE_SWITCH_ENUM(IMAPTEST
);
53 #define X(sw, minchars, id) { sw, minchars, id },
54 DEFINE_SWITCH_ARRAY(IMAPTEST
, switches
);
60 char *command
; /* Command to send */
61 char *folder
; /* Folder (for append) */
62 bool queue
; /* If true, queue for later delivery */
63 bool append
; /* If true, append "command" to mbox */
64 size_t msgsize
; /* RFC822 size of message */
65 struct imap_msg
*next
; /* Next pointer */
68 struct imap_msg
*msgqueue_head
= NULL
;
69 struct imap_msg
*msgqueue_tail
= NULL
;
74 char tag
[16]; /* Command tag */
75 struct timeval start
; /* Time command was sent */
76 char prefix
[64]; /* Command prefix */
77 struct imap_cmd
*next
; /* Next pointer */
80 static struct imap_cmd
*cmdqueue
= NULL
;
82 static char *saslmechs
= NULL
;
83 svector_t imap_capabilities
= NULL
;
85 static int imap_sasl_callback(enum sasl_message_type
, unsigned const char *,
86 unsigned int, unsigned char **, unsigned int *,
89 static void parse_capability(const char *, unsigned int len
);
90 static int capability_set(const char *);
91 static void clear_capability(void);
92 static int have_capability(void);
93 static int send_imap_command(netsec_context
*, bool noflush
, char **errstr
,
94 const char *fmt
, ...) CHECK_PRINTF(4, 5);
95 static int send_append(netsec_context
*, struct imap_msg
*, char **errstr
);
96 static int get_imap_response(netsec_context
*, const char *token
,
97 char **tokenresp
, char **status
, bool contok
,
98 bool failerr
, char **errstr
);
100 static void ts_report(struct timeval
*tv
, const char *fmt
, ...)
102 static void imap_negotiate_tls(netsec_context
*);
104 static void add_msg(bool queue
, struct imap_msg
**, const char *fmt
, ...)
106 static void add_append(const char *filename
, const char *folder
, bool queue
);
108 static void batchfile(const char *filename
, char *afolder
, bool queue
);
110 static size_t rfc822size(const char *filename
);
112 static bool timestamp
= false;
115 main (int argc
, char **argv
)
117 bool sasl
= false, tls
= false, initialtls
= false;
118 bool snoop
= false, queue
= false;
120 char *saslmech
= NULL
, *host
= NULL
, *port
= "143", *user
= NULL
;
121 char *cp
, **argp
, buf
[BUFSIZ
], *oauth_svc
= NULL
, *errstr
, **arguments
, *p
;
122 char *afolder
= NULL
;
123 netsec_context
*nsc
= NULL
;
124 struct imap_msg
*imsg
;
126 struct timeval tv_start
, tv_connect
, tv_auth
;
128 if (nmh_init(argv
[0], true, true)) { return 1; }
130 arguments
= getarguments (invo_name
, argc
, argv
, 1);
133 while ((cp
= *argp
++)) {
135 switch (smatch (++cp
, switches
)) {
137 ambigsw (cp
, switches
);
140 die("-%s unknown", cp
);
143 snprintf (buf
, sizeof(buf
), "%s [switches] +folder command "
144 "[command ...]", invo_name
);
145 print_help (buf
, switches
, 1);
148 print_version(invo_name
);
152 if (!(host
= *argp
++) || *host
== '-')
153 die("missing argument to %s", argp
[-2]);
156 if (!(port
= *argp
++) || *port
== '-')
157 die("missing argument to %s", argp
[-2]);
160 if (!(user
= *argp
++) || *user
== '-')
161 die("missing argument to %s", argp
[-2]);
165 if (!(afolder
= *argp
++) || *afolder
== '-')
166 die("missing argument to %s", argp
[-2]);
169 if (!*argp
|| (**argp
== '-'))
170 die("missing argument to %s", argp
[-1]);
172 die("Append folder must be set with -afolder first");
173 add_append(*argp
++, afolder
, queue
);
177 if (! *argp
|| (**argp
== '-'))
178 die("missing argument to %s", argp
[-1]);
179 batchfile(*argp
++, afolder
, queue
);
183 if (! *argp
|| (**argp
== '-'))
184 die("missing argument to %s", argp
[-1]);
185 if (! (timeout
= atoi(*argp
++)))
186 die("Invalid timeout: %s", argp
[-1]);
202 if (!(saslmech
= *argp
++) || *saslmech
== '-')
203 die("missing argument to %s", argp
[-2]);
206 if (!(oauth_svc
= *argp
++) || *oauth_svc
== '-')
207 die("missing argument to %s", argp
[-2]);
234 } else if (*cp
== '+') {
235 if (*(cp
+ 1) == '\0')
236 die("Invalid null folder name");
237 add_msg(false, NULL
, "SELECT \"%s\"", cp
+ 1);
239 add_msg(queue
, NULL
, "%s", cp
);
244 die("A hostname must be given with -host");
249 netsec_set_userid(nsc
, user
);
251 netsec_set_hostname(nsc
, host
);
254 netsec_set_timeout(nsc
, timeout
);
257 netsec_set_snoop(nsc
, 1);
260 if (netsec_set_oauth_service(nsc
, oauth_svc
) != OK
) {
261 die("OAuth2 not supported");
266 gettimeofday(&tv_start
, NULL
);
268 if ((fd
= client(host
, port
, buf
, sizeof(buf
), snoop
)) == NOTOK
)
269 die("Connect failed: %s", buf
);
272 ts_report(&tv_start
, "Connect time");
273 gettimeofday(&tv_connect
, NULL
);
276 netsec_set_fd(nsc
, fd
, fd
);
277 netsec_set_snoop(nsc
, snoop
);
279 if (initialtls
|| tls
) {
280 if (netsec_set_tls(nsc
, 1, 0, &errstr
) != OK
)
284 imap_negotiate_tls(nsc
);
288 if (netsec_set_sasl_params(nsc
, "imap", saslmech
, imap_sasl_callback
,
293 if ((cp
= netsec_readline(nsc
, &len
, &errstr
)) == NULL
) {
297 if (has_prefix(cp
, "* BYE")) {
298 fprintf(stderr
, "Connection rejected: %s\n", cp
+ 5);
302 if (!has_prefix(cp
, "* OK") && !has_prefix(cp
, "* PREAUTH")) {
303 fprintf(stderr
, "Invalid server response: %s\n", cp
);
307 if ((p
= strchr(cp
+ 2, ' ')) && *(p
+ 1) != '\0' &&
308 has_prefix(p
+ 1, "[CAPABILITY ")) {
310 * Parse the capability list out to the end
313 p
+= 13; /* 1 + [CAPABILITY + space */
315 if (!(q
= strchr(p
, ']'))) {
316 fprintf(stderr
, "Cannot find end of CAPABILITY announcement\n");
320 parse_capability(p
, q
- p
);
324 if (send_imap_command(nsc
, false, &errstr
, "CAPABILITY") != OK
) {
325 fprintf(stderr
, "Unable to send CAPABILITY command: %s\n", errstr
);
329 if (get_imap_response(nsc
, "CAPABILITY ", &capstring
, NULL
, false, true,
331 fprintf(stderr
, "Cannot get CAPABILITY response: %s\n", errstr
);
336 fprintf(stderr
, "No CAPABILITY response seen\n");
340 p
= capstring
+ 11; /* "CAPABILITY " */
342 parse_capability(p
, strlen(p
));
347 if (!capability_set("STARTTLS")) {
348 fprintf(stderr
, "Requested STARTTLS with -tls, but IMAP server "
349 "has no support for STARTTLS\n");
352 if (send_imap_command(nsc
, false, &errstr
, "STARTTLS") != OK
) {
353 fprintf(stderr
, "Unable to issue STARTTLS: %s\n", errstr
);
357 if (get_imap_response(nsc
, NULL
, NULL
, NULL
, false, true,
359 fprintf(stderr
, "STARTTLS failed: %s\n", errstr
);
363 imap_negotiate_tls(nsc
);
367 if (netsec_negotiate_sasl(nsc
, saslmechs
, &errstr
) != OK
) {
368 fprintf(stderr
, "SASL negotiation failed: %s\n", errstr
);
372 * Sigh. If we negotiated a SSF AND we got a capability response
373 * as part of the AUTHENTICATE OK message, we can't actually trust
374 * it because it's not protected at that point. So discard the
375 * capability list and we will generate a fresh CAPABILITY command
378 if (netsec_get_sasl_ssf(nsc
) > 0) {
382 if (capability_set("LOGINDISABLED")) {
383 fprintf(stderr
, "User did not request SASL, but LOGIN "
389 if (!have_capability()) {
392 if (send_imap_command(nsc
, false, &errstr
, "CAPABILITY") != OK
) {
393 fprintf(stderr
, "Unable to send CAPABILITY command: %s\n", errstr
);
397 if (get_imap_response(nsc
, "CAPABILITY ", &capstring
, NULL
, false,
398 true, &errstr
) != OK
) {
399 fprintf(stderr
, "Cannot get CAPABILITY response: %s\n", errstr
);
404 fprintf(stderr
, "No CAPABILITY response seen\n");
408 p
= capstring
+ 11; /* "CAPABILITY " */
410 parse_capability(p
, strlen(p
));
415 ts_report(&tv_connect
, "Authentication time");
416 gettimeofday(&tv_auth
, NULL
);
419 while (msgqueue_head
!= NULL
) {
420 imsg
= msgqueue_head
;
423 if (send_append(nsc
, imsg
, &errstr
) != OK
) {
424 fprintf(stderr
, "Cannot send APPEND for %s to mbox %s: %s\n",
425 imsg
->command
, imsg
->folder
, errstr
);
429 } else if (send_imap_command(nsc
, imsg
->queue
, &errstr
, "%s",
430 imsg
->command
) != OK
) {
431 fprintf(stderr
, "Cannot send command \"%s\": %s\n",
432 imsg
->command
, errstr
);
438 if (get_imap_response(nsc
, NULL
, NULL
, NULL
, false, false,
440 fprintf(stderr
, "Unable to get response for command "
441 "\"%s\": %s\n", imsg
->command
, errstr
);
446 msgqueue_head
= imsg
->next
;
454 * Flush out any pending network data and get any responses
457 if (netsec_flush(nsc
, &errstr
) != OK
) {
458 fprintf(stderr
, "Error performing final network flush: %s\n", errstr
);
462 if (get_imap_response(nsc
, NULL
, NULL
, NULL
, false, false, &errstr
) != OK
) {
463 fprintf(stderr
, "Error fetching final command responses: %s\n", errstr
);
468 ts_report(&tv_auth
, "Total command execution time");
470 send_imap_command(nsc
, false, NULL
, "LOGOUT");
471 get_imap_response(nsc
, NULL
, NULL
, NULL
, false, false, NULL
);
474 netsec_shutdown(nsc
);
477 ts_report(&tv_start
, "Total elapsed time");
483 * Parse a capability string
485 * Put these into an svector so we can check for stuff later. But special
486 * things, like AUTH, we parse now
490 parse_capability(const char *cap
, unsigned int len
)
492 char *str
= mh_xmalloc(len
+ 1);
496 trunccpy(str
, cap
, len
+ 1);
497 caplist
= brkstring(str
, " ", NULL
);
499 if (imap_capabilities
) {
500 svector_free(imap_capabilities
);
503 imap_capabilities
= svector_create(32);
510 for (i
= 0; caplist
[i
] != NULL
; i
++) {
511 if (has_prefix(caplist
[i
], "AUTH=") && *(caplist
[i
] + 5) != '\0') {
513 saslmechs
= add(" ", saslmechs
);
515 saslmechs
= add(caplist
[i
] + 5, saslmechs
);
517 svector_push_back(imap_capabilities
, getcpy(caplist
[i
]));
525 * Return 1 if a particular capability is set
529 capability_set(const char *capability
)
531 if (!imap_capabilities
)
534 return svector_find(imap_capabilities
, capability
) != NULL
;
538 * Clear the CAPABILITY list (used after we call STARTTLS, for example)
542 clear_capability(void)
544 svector_free(imap_capabilities
);
545 imap_capabilities
= NULL
;
549 have_capability(void)
551 return imap_capabilities
!= NULL
;
555 * Our SASL callback, which handles the SASL authentication dialog
559 imap_sasl_callback(enum sasl_message_type mtype
, unsigned const char *indata
,
560 unsigned int indatalen
, unsigned char **outdata
,
561 unsigned int *outdatalen
, void *context
, char **errstr
)
564 char *mech
, *line
, *capstring
, *p
;
566 netsec_context
*nsc
= (netsec_context
*) context
;
569 case NETSEC_SASL_START
:
571 * Generate our AUTHENTICATE message.
573 * If SASL-IR capability is set, we can include any initial response
574 * in the AUTHENTICATE command. Otherwise we have to wait for
575 * the first server response (which should be blank).
578 mech
= netsec_get_sasl_mechanism(nsc
);
582 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
583 writeBase64raw(indata
, indatalen
, (unsigned char *) b64data
);
584 if (capability_set("SASL-IR")) {
585 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
,
588 * Sigh. We're assuming a short tag length here. Really,
589 * I guess we should figure out a way to get the length
592 snoopoffset
= 17 + strlen(mech
);
593 rc
= send_imap_command(nsc
, 0, errstr
, "AUTHENTICATE %s %s",
596 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
600 rc
= send_imap_command(nsc
, 0, errstr
, "AUTHENTICATE %s", mech
);
601 line
= netsec_readline(nsc
, &len
, errstr
);
605 * We should get a "+ ", nothing else.
607 if (len
!= 2 || strcmp(line
, "+ ") != 0) {
608 netsec_err(errstr
, "Did not get expected blank response "
609 "for initial challenge response");
612 rc
= netsec_printf(nsc
, errstr
, "%s\r\n", b64data
);
616 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
617 rc
= netsec_flush(nsc
, errstr
);
618 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
623 if (send_imap_command(nsc
, 0, errstr
, "AUTHENTICATE %s",
631 * Get a response, decode it and process it.
634 case NETSEC_SASL_READ
:
635 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, &snoopoffset
);
637 line
= netsec_readline(nsc
, &len
, errstr
);
638 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
643 if (len
< 2 || (len
== 2 && strcmp(line
, "+ ") != 0)) {
644 netsec_err(errstr
, "Invalid format for SASL response");
652 rc
= decodeBase64(line
+ 2, outdata
, &len
, 0, NULL
);
655 netsec_err(errstr
, "Unable to decode base64 response");
662 * Simple request encoding
665 case NETSEC_SASL_WRITE
:
667 unsigned char *b64data
;
668 b64data
= mh_xmalloc(BASE64SIZE(indatalen
));
669 writeBase64raw(indata
, indatalen
, b64data
);
670 rc
= netsec_printf(nsc
, errstr
, "%s", b64data
);
676 if (netsec_printf(nsc
, errstr
, "\r\n") != OK
)
679 netsec_set_snoop_callback(nsc
, netsec_b64_snoop_decoder
, NULL
);
680 rc
= netsec_flush(nsc
, errstr
);
681 netsec_set_snoop_callback(nsc
, NULL
, NULL
);
690 case NETSEC_SASL_FINISH
:
692 if (get_imap_response(nsc
, "CAPABILITY ", &capstring
, &line
,
693 false, true, errstr
) != OK
)
696 * We MIGHT get a capability response here. If so, be sure we
697 * parse it. We ALSO might get a untagged CAPABILITY response.
698 * Which one should we prefer? I guess we'll go with the untagged
703 p
= capstring
+ 11; /* "CAPABILITY " */
704 parse_capability(p
, strlen(p
));
706 } else if (line
&& has_prefix(line
, "OK [CAPABILITY ")) {
713 parse_capability(p
, q
- p
);
719 * Cancel an authentication dialog
722 case NETSEC_SASL_CANCEL
:
723 rc
= netsec_printf(nsc
, errstr
, "*\r\n");
725 rc
= netsec_flush(nsc
, errstr
);
734 * Send a single command to the IMAP server.
738 send_imap_command(netsec_context
*nsc
, bool noflush
, char **errstr
,
739 const char *fmt
, ...)
741 static unsigned int seq
= 0; /* Tag sequence number */
744 struct imap_cmd
*cmd
;
746 cmd
= mh_xmalloc(sizeof(*cmd
));
748 snprintf(cmd
->tag
, sizeof(cmd
->tag
), "A%u ", seq
++);
753 vsnprintf(cmd
->prefix
, sizeof(cmd
->prefix
), fmt
, ap
);
756 p
= strchr(cmd
->prefix
, ' ');
761 gettimeofday(&cmd
->start
, NULL
);
764 if (netsec_write(nsc
, cmd
->tag
, strlen(cmd
->tag
), errstr
) != OK
) {
770 rc
= netsec_vprintf(nsc
, errstr
, fmt
, ap
);
774 rc
= netsec_write(nsc
, "\r\n", 2, errstr
);
781 if (!noflush
&& netsec_flush(nsc
, errstr
) != OK
) {
786 cmd
->next
= cmdqueue
;
793 * Send an APPEND to the server, which requires some extra semantics
797 send_append(netsec_context
*nsc
, struct imap_msg
*imsg
, char **errstr
)
800 bool nonsynlit
= false;
801 char *status
= NULL
, *line
= NULL
;
806 * If we have the LITERAL+ extension, or we have LITERAL- and our
807 * message is 4096 bytes or less, we can do it all in one message.
808 * Otherwise we have to wait for a contination marker (+).
811 if (capability_set("LITERAL+") ||
812 (capability_set("LITERAL-") && imsg
->msgsize
<= 4096)) {
817 * Send our APPEND command
820 if (send_imap_command(nsc
, nonsynlit
, errstr
, "APPEND \"%s\" {%u%s}",
821 imsg
->folder
, (unsigned int) imsg
->msgsize
,
822 nonsynlit
? "+" : "") != OK
)
826 * If we need to wait for a syncing literal, do that now
830 if (get_imap_response(nsc
, NULL
, NULL
, &status
, true,
831 true, errstr
) != OK
) {
832 imsg
->queue
= true; /* XXX Sigh */
833 fprintf(stderr
, "APPEND command failed: %s\n", *errstr
);
837 if (!(status
&& has_prefix(status
, "+"))) {
838 netsec_err(errstr
, "Expected contination (+), but got: %s", status
);
846 * Now write the message out, but make sure we end each line with \r\n
849 if ((f
= fopen(imsg
->command
, "r")) == NULL
) {
850 netsec_err(errstr
, "Unable to open %s: %s", imsg
->command
,
855 while ((rc
= getline(&line
, &linesize
, f
)) > 0) {
856 if (rc
> 1 && line
[rc
- 1] == '\n' && line
[rc
- 2] == '\r') {
857 if (netsec_write(nsc
, line
, rc
, errstr
) != OK
) {
863 if (line
[rc
- 1] == '\n')
865 if (netsec_write(nsc
, line
, rc
, errstr
) != OK
) {
870 if (netsec_write(nsc
, "\r\n", 2, errstr
) != OK
) {
881 netsec_err(errstr
, "Error reading %s: %s", imsg
->command
,
890 * Send a final \r\n for the end of the command
893 if (netsec_write(nsc
, "\r\n", 2, errstr
) != OK
)
897 return netsec_flush(nsc
, errstr
);
903 * Get all outstanding responses. If we were passed in a token string
904 * to look for, return it.
908 get_imap_response(netsec_context
*nsc
, const char *token
, char **tokenresponse
,
909 char **status
, bool condok
, bool failerr
, char **errstr
)
912 struct imap_cmd
*cmd
;
913 bool numerrs
= false;
916 *tokenresponse
= NULL
;
920 if (!(line
= netsec_readline(nsc
, NULL
, errstr
)))
922 if (has_prefix(line
, "* ") && *(line
+ 2) != '\0') {
923 if (token
&& tokenresponse
&& has_prefix(line
+ 2, token
)) {
925 free(*tokenresponse
);
926 *tokenresponse
= getcpy(line
+ 2);
928 } if (condok
&& has_prefix(line
, "+")) {
930 *status
= getcpy(line
);
933 * Special case; return now but don't dequeue the tag,
934 * since we will want to get final result later.
939 if (has_prefix(line
, cmdqueue
->tag
)) {
942 ts_report(&cmd
->start
, "Command (%s) execution time",
944 cmdqueue
= cmd
->next
;
947 for (cmd
= cmdqueue
; cmd
->next
!= NULL
; cmd
= cmd
->next
) {
948 if (has_prefix(line
, cmd
->next
->tag
)) {
949 struct imap_cmd
*cmd2
= cmd
->next
;
950 cmd
->next
= cmd
->next
->next
;
951 if (failerr
&& strncmp(line
+ strlen(cmd2
->tag
),
954 netsec_err(errstr
, "%s", line
+ strlen(cmd2
->tag
));
957 ts_report(&cmd2
->start
, "Command (%s) execution "
958 "time", cmd2
->prefix
);
961 *status
= getcpy(line
);
969 return numerrs
? NOTOK
: OK
;
973 * Add an IMAP command to the msg queue
977 add_msg(bool queue
, struct imap_msg
**ret_imsg
, const char *fmt
, ...)
979 struct imap_msg
*imsg
;
987 msg
= mh_xrealloc(msg
, msgbufsize
);
989 rc
= vsnprintf(msg
, msgbufsize
, fmt
, ap
);
991 } while (rc
>= (int) msgbufsize
);
993 imsg
= mh_xmalloc(sizeof(*imsg
));
997 imsg
->append
= false;
1001 if (msgqueue_head
== NULL
) {
1002 msgqueue_head
= imsg
;
1003 msgqueue_tail
= imsg
;
1005 msgqueue_tail
->next
= imsg
;
1006 msgqueue_tail
= imsg
;
1014 * Add an APPEND command to the queue
1018 add_append(const char *filename
, const char *folder
, bool queue
)
1020 size_t filesize
= rfc822size(filename
);
1021 struct imap_msg
*imsg
;
1023 add_msg(queue
, &imsg
, "%s", filename
);
1025 imsg
->folder
= getcpy(folder
);
1026 imsg
->append
= true;
1027 imsg
->msgsize
= filesize
;
1031 * Process a batch file, which can contain commands (and some arguments)
1035 batchfile(const char *filename
, char *afolder
, bool queue
)
1039 size_t linesize
= 0;
1041 bool afolder_alloc
= false;
1043 if (!(f
= fopen(filename
, "r"))) {
1044 die("Unable to open batch file %s: %s", filename
, strerror(errno
));
1047 while ((rc
= getline(&line
, &linesize
, f
)) > 0) {
1048 line
[rc
- 1] = '\0';
1050 switch (smatch (line
+ 1, switches
)) {
1060 rc
= getline(&line
, &linesize
, f
);
1062 die("Unable to read next line for -afolder");
1064 die("Folder name cannot be blank");
1065 line
[rc
- 1] = '\0';
1066 afolder
= getcpy(line
);
1067 afolder_alloc
= true;
1071 rc
= getline(&line
, &linesize
, f
);
1073 die("Unable to read filename for -append");
1075 die("Filename for -append cannot be blank");
1076 line
[rc
- 1] = '\0';
1077 add_append(line
, afolder
, queue
);
1081 ambigsw (line
, switches
);
1084 die("%s unknown", line
);
1086 die("Switch %s not supported in batch mode", line
);
1088 } else if (*line
== '+') {
1089 if (*(line
+ 1) == '\0')
1090 die("Invalid null folder name");
1091 add_msg(false, NULL
, "SELECT \"%s\"", line
+ 1);
1094 continue; /* Ignore blank line */
1095 add_msg(queue
, NULL
, "%s", line
);
1100 die("Read of \"%s\" failed: %s", filename
, strerror(errno
));
1112 * Negotiate TLS connection, with optional timestamp
1116 imap_negotiate_tls(netsec_context
*nsc
)
1122 gettimeofday(&tv
, NULL
);
1124 if (netsec_negotiate_tls(nsc
, &errstr
) != OK
)
1128 ts_report(&tv
, "TLS negotation time");
1132 * Give a timestamp report.
1136 ts_report(struct timeval
*tv
, const char *fmt
, ...)
1142 gettimeofday(&now
, NULL
);
1144 delta
= ((double) now
.tv_sec
) - ((double) tv
->tv_sec
) +
1145 (now
.tv_usec
/ 1E6
- tv
->tv_usec
/ 1E6
);
1148 vfprintf(stdout
, fmt
, ap
);
1151 printf(": %f sec\n", delta
);
1155 * Calculate the RFC 822 size of file.
1159 rfc822size(const char *filename
)
1162 size_t total
= 0, linecap
= 0;
1166 if (! (f
= fopen(filename
, "r")))
1167 die("Unable to open %s: %s", filename
, strerror(errno
));
1169 while ((rc
= getline(&line
, &linecap
, f
)) > 0) {
1171 if (line
[rc
- 1] == '\n' && (rc
== 1 || line
[rc
- 2] != '\r'))
1173 if (line
[rc
- 1] != '\n')
1180 die("Error while reading %s: %s", filename
, strerror(errno
));