Set properties on the uploaded resource.
Test 3209 and 3210 verify.
Closes #15970
9. IMAP
9.1 Enhanced capability support
- 9.2 upload unread
10. LDAP
10.1 SASL based authentication mechanisms
Add the ability, for an application that uses libcurl, to obtain the list of
capabilities returned from the CAPABILITY command.
-9.2 upload unread
-
- Uploads over IMAP currently always set the email as "read" (or "seen"). It
- would be good to offer a way for users to select for uploads to remain
- unread.
-
10. LDAP
10.1 SASL based authentication mechanisms
trace.md \
unix-socket.md \
upload-file.md \
+ upload-flags.md \
url.md \
url-query.md \
use-ascii.md \
to use.
When putting the local filename at the end of the URL, curl ignores what is on
-the left side of any slash (/) or backslash (\) used in the filename and only
+the left side of any slash (/) or backslash (\\) used in the filename and only
appends what is on the right side of the rightmost such character.
Use the filename `-` (a single dash) to use stdin instead of a given file.
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Long: upload-flags
+Arg: <flags>
+Help: IMAP upload behavior
+Category: curl output
+Added: 8.13.0
+Multi: single
+See-also:
+ - upload-file
+Example:
+ - --upload-flags Flagged,!Seen --upload-file local/dir/file $URL
+---
+
+# `--upload-flags`
+
+Specify additional behavior to apply to uploaded files. Flags are
+specified as either a single flag value or a comma-separated list
+of flag values. These values are case-sensitive and may be negated
+by prepending them with a '-' character. Currently the following
+flag values are accepted: answered, deleted, draft, flagged, and
+seen. The currently-accepted flag values are used to set flags on
+IMAP uploads.
~~~
Get a "quoted" word. This means everything that is provided within a leading
-and an ending double character. No escaping possible.
+and an ending double quote character. No escaping possible.
`max` is the longest accepted word, or it returns error.
Set upload buffer size. See CURLOPT_UPLOAD_BUFFERSIZE(3)
+## CURLOPT_UPLOAD_FLAGS
+
+Set upload flags. See CURLOPT_UPLOAD_FLAGS(3)
+
## CURLOPT_URL
URL to work on. See CURLOPT_URL(3)
--- /dev/null
+---
+c: Copyright (C) Daniel Stenberg, <daniel.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLOPT_UPLOAD_FLAGS
+Section: 3
+Source: libcurl
+See-also:
+ - CURLOPT_UPLOAD (3)
+Protocol:
+ - IMAP
+ - IMAPS
+Added-in: 8.13.0
+---
+
+# NAME
+
+CURLOPT_UPLOAD_FLAGS - upload flags for IMAP
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_UPLOAD_FLAGS, long bitmask);
+~~~
+
+# DESCRIPTION
+
+Pass a long as parameter, which is set to a bitmask, to tell libcurl which
+flags to send the server relating to uploaded files. The current supported
+flags are **CURLULFLAG_ANSWERED**, which sets the **Answered** flag for IMAP
+uploads, **CURLULFLAG_DELETED**, which sets the **Deleted** flag for IMAP
+uploads, **CURLULFLAG_DRAFT**, which sets the **Draft** flag for IMAP uploads,
+**CURLULFLAG_FLAGGED**, which sets the **Flagged** flag for IMAP uploads, and
+**CURLULFLAG_SEEN**, which sets the **Seen** flag for IMAP uploads.
+
+# DEFAULT
+
+A bitmask with only the **CURLULFLAG_SEEN** flag set.
+
+# %PROTOCOLS%
+
+# EXAMPLE
+
+~~~c
+static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ FILE *src = userdata;
+ /* copy as much data as possible into the 'ptr' buffer, but no more than
+ 'size' * 'nmemb' bytes */
+ size_t retcode = fread(ptr, size, nmemb, src);
+
+ return retcode;
+}
+
+int main(void)
+{
+ CURL *curl = curl_easy_init();
+ if(curl) {
+ FILE *src = fopen("local-file", "r");
+ curl_off_t fsize; /* set this to the size of the input file */
+
+ /* we want to use our own read function */
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb);
+
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
+ /* specify target */
+ curl_easy_setopt(curl, CURLOPT_URL, "imap://example.com:993/mailbox");
+
+ /* provide username */
+ curl_easy_setopt(curl, CURLOPT_USERNAME, "user@example.com");
+
+ /* provide password */
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, "password");
+
+ /* specify that uploaded mail should be considered flagged */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD_FLAGS, CURLULFLAG_FLAGGED);
+
+ /* now specify which pointer to pass to our callback */
+ curl_easy_setopt(curl, CURLOPT_READDATA, src);
+
+ /* Set the size of the file to upload */
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize);
+
+ /* perform the upload */
+ curl_easy_perform(curl);
+ }
+}
+~~~
+
+# %AVAILABILITY%
+
+# RETURN VALUE
+
+curl_easy_setopt(3) returns a CURLcode indicating success or error.
+
+CURLE_OK (0) means everything was OK, non-zero means an error occurred, see
+libcurl-errors(3).
CURLOPT_UPKEEP_INTERVAL_MS.3 \
CURLOPT_UPLOAD.3 \
CURLOPT_UPLOAD_BUFFERSIZE.3 \
+ CURLOPT_UPLOAD_FLAGS.3 \
CURLOPT_URL.3 \
CURLOPT_USE_SSL.3 \
CURLOPT_USERAGENT.3 \
CURLOPT_UPKEEP_INTERVAL_MS 7.62.0
CURLOPT_UPLOAD 7.1
CURLOPT_UPLOAD_BUFFERSIZE 7.62.0
+CURLOPT_UPLOAD_FLAGS 8.13.0
CURLOPT_URL 7.1
CURLOPT_USE_SSL 7.17.0
CURLOPT_USERAGENT 7.1
CURLUPART_URL 7.62.0
CURLUPART_USER 7.62.0
CURLUPART_ZONEID 7.65.0
+CURLULFLAG_ANSWERED 8.13.0
+CURLULFLAG_DELETED 8.13.0
+CURLULFLAG_DRAFT 8.13.0
+CURLULFLAG_FLAGGED 8.13.0
+CURLULFLAG_SEEN 8.13.0
CURLUSESSL_ALL 7.17.0
CURLUSESSL_CONTROL 7.17.0
CURLUSESSL_NONE 7.17.0
--trace-time 7.14.0
--unix-socket 7.40.0
--upload-file (-T) 4.0
+--upload-flags 8.13.0
--url 7.5
--url-query 7.87.0
--use-ascii (-B) 5.0
#define CURLALTSVC_H2 (1<<4)
#define CURLALTSVC_H3 (1<<5)
+/* bitmask values for CURLOPT_UPLOAD_FLAGS */
+#define CURLULFLAG_ANSWERED (1L<<0)
+#define CURLULFLAG_DELETED (1L<<1)
+#define CURLULFLAG_DRAFT (1L<<2)
+#define CURLULFLAG_FLAGGED (1L<<3)
+#define CURLULFLAG_SEEN (1L<<4)
struct curl_hstsentry {
char *name;
/* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */
CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326),
+ CURLOPT(CURLOPT_UPLOAD_FLAGS, CURLOPTTYPE_LONG, 327),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
{"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0},
{"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0},
{"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0},
+ {"UPLOAD_FLAGS", CURLOPT_UPLOAD_FLAGS, CURLOT_LONG, 0},
{"URL", CURLOPT_URL, CURLOT_STRING, 0},
{"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0},
{"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0},
*/
int Curl_easyopts_check(void)
{
- return (CURLOPT_LASTENTRY % 10000) != (326 + 1);
+ return (CURLOPT_LASTENTRY % 10000) != (327 + 1);
}
#endif
***************************************************************************/
#include "curl_setup.h"
+#include "dynbuf.h"
#ifndef CURL_DISABLE_IMAP
SASL_FLAG_BASE64 /* Configuration flags */
};
+struct ulbits {
+ int bit;
+ const char *flag;
+};
/***********************************************************************
*
CURLcode result = CURLE_OK;
struct IMAP *imap = data->req.p.imap;
char *mailbox;
+ struct dynbuf flags;
/* Check we have a mailbox */
if(!imap->mailbox) {
if(!mailbox)
return CURLE_OUT_OF_MEMORY;
- /* Send the APPEND command */
- result = imap_sendf(data, "APPEND %s (\\Seen) {%" FMT_OFF_T "}",
- mailbox, data->state.infilesize);
+ /* Generate flags string and send the APPEND command */
+ Curl_dyn_init(&flags, 100);
+ if(data->set.upload_flags) {
+ int i;
+ struct ulbits ulflag[] = {
+ {CURLULFLAG_ANSWERED, "Answered"},
+ {CURLULFLAG_DELETED, "Deleted"},
+ {CURLULFLAG_DRAFT, "Draft"},
+ {CURLULFLAG_FLAGGED, "Flagged"},
+ {CURLULFLAG_SEEN, "Seen"},
+ {0, NULL}
+ };
+
+ result = CURLE_OUT_OF_MEMORY;
+ if(Curl_dyn_add(&flags, " (")) {
+ goto cleanup;
+ }
+
+ for(i = 0; ulflag[i].bit; i++) {
+ if(data->set.upload_flags & ulflag[i].bit) {
+ if((Curl_dyn_len(&flags) > 2 && Curl_dyn_add(&flags, " ")) ||
+ Curl_dyn_add(&flags, "\\") || Curl_dyn_add(&flags, ulflag[i].flag))
+ goto cleanup;
+ }
+ }
+
+ if(Curl_dyn_add(&flags, ")"))
+ goto cleanup;
+ }
+ else if(Curl_dyn_add(&flags, ""))
+ goto cleanup;
+
+ result = imap_sendf(data, "APPEND %s%s {%" FMT_OFF_T "}",
+ mailbox, Curl_dyn_ptr(&flags), data->state.infilesize);
+cleanup:
+ Curl_dyn_free(&flags);
free(mailbox);
if(!result)
*/
Curl_safefree(data->set.str[STRING_SSL_ENGINE]);
return Curl_ssl_set_engine_default(data);
-
+ case CURLOPT_UPLOAD_FLAGS:
+ data->set.upload_flags = (unsigned char)arg;
+ break;
default:
/* unknown option */
return CURLE_UNKNOWN_OPTION;
to be used in the library's request(s) */
unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
file 0 - whatever, 1 - v2, 2 - v6 */
+ unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */
#ifdef HAVE_GSSAPI
/* GSS-API credential delegation, see the documentation of
CURLOPT_GSSAPI_DELEGATION */
config->http09_allowed = FALSE;
config->ftp_skip_ip = TRUE;
config->file_clobber_mode = CLOBBER_DEFAULT;
+ config->upload_flags = CURLULFLAG_SEEN;
curlx_dyn_init(&config->postdata, MAX_FILE2MEMORY);
}
CLOBBER_NEVER, /* If the file exists, always fail */
CLOBBER_ALWAYS /* If the file exists, always overwrite it */
} file_clobber_mode;
+ unsigned char upload_flags; /* Bitmask for --upload-flags */
unsigned short porttouse;
BIT(remote_time);
BIT(cookiesession); /* new session? */
{"trace-time", ARG_BOOL, ' ', C_TRACE_TIME},
{"unix-socket", ARG_FILE, ' ', C_UNIX_SOCKET},
{"upload-file", ARG_FILE, 'T', C_UPLOAD_FILE},
+ {"upload-flags", ARG_STRG, ' ', C_UPLOAD_FLAGS},
{"url", ARG_STRG, ' ', C_URL},
{"url-query", ARG_STRG, ' ', C_URL_QUERY},
{"use-ascii", ARG_BOOL, 'B', C_USE_ASCII},
return err;
}
+struct flagmap {
+ const char *name;
+ size_t len;
+ unsigned char flag;
+};
+
+static const struct flagmap flag_table[] = {
+ {"answered", 8, CURLULFLAG_ANSWERED},
+ {"deleted", 7, CURLULFLAG_DELETED},
+ {"draft", 5, CURLULFLAG_DRAFT},
+ {"flagged", 7, CURLULFLAG_FLAGGED},
+ {"seen", 4, CURLULFLAG_SEEN},
+ {NULL, 0, 0}
+};
+
+static ParameterError parse_upload_flags(struct OperationConfig *config,
+ char *nextarg)
+{
+ char *flag;
+ ParameterError err = PARAM_OK;
+ char *tmp = strdup(nextarg);
+
+ if(!tmp)
+ return PARAM_NO_MEM;
+
+ flag = tmp;
+ while(flag) {
+ bool negate;
+ const struct flagmap *map;
+ char *next = strchr(flag, ','); /* Find next comma or end */
+ if(next)
+ *next++ = '\0';
+
+ negate = (*flag == '-');
+ if(negate)
+ flag++;
+
+ for(map = flag_table; map->name; map++) {
+ if(!strncmp(flag, map->name, map->len) && flag[map->len] == '\0') {
+ if(negate)
+ config->upload_flags &= (unsigned char)~map->flag;
+ else
+ config->upload_flags |= map->flag;
+ break;
+ }
+ }
+
+ if(!map->name) {
+ err = PARAM_OPTION_UNKNOWN;
+ break;
+ }
+
+ flag = next;
+ }
+
+ free(tmp);
+ return err;
+}
+
ParameterError getparameter(const char *flag, /* f or -long-flag */
char *nextarg, /* NULL if unset */
argv_item_t cleararg1,
case C_MPTCP: /* --mptcp */
config->mptcp = TRUE;
break;
+ case C_UPLOAD_FLAGS: /* --upload-flags */
+ err = parse_upload_flags(config, nextarg);
+ break;
default: /* unknown flag */
err = PARAM_OPTION_UNKNOWN;
break;
C_IP_TOS,
C_UNIX_SOCKET,
C_UPLOAD_FILE,
+ C_UPLOAD_FLAGS,
C_URL,
C_URL_QUERY,
C_USE_ASCII,
{"-T, --upload-file <file>",
"Transfer local FILE to destination",
CURLHELP_IMPORTANT | CURLHELP_UPLOAD},
+ {" --upload-flags <flags>",
+ "IMAP upload behavior",
+ CURLHELP_CURL | CURLHELP_OUTPUT},
{" --url <url/file>",
"URL(s) to work with",
CURLHELP_CURL},
}
#endif
}
+ /* new in 8.13.0 */
+ if(config->upload_flags)
+ my_setopt(curl, CURLOPT_UPLOAD_FLAGS, (long)config->upload_flags);
return result;
}
NV1(CURLOPT_PROXY_SSL_VERIFYPEER, 1),
NV1(CURLOPT_PROXY_SSL_VERIFYHOST, 1),
NV1(CURLOPT_SOCKS5_AUTH, 1),
+ NV1(CURLOPT_UPLOAD_FLAGS, CURLULFLAG_SEEN),
NVEND
};
\
test3100 test3101 test3102 test3103 test3104 test3105 \
test3200 \
-test3201 test3202 test3203 test3204 test3205 test3207 test3208
-
+test3201 test3202 test3203 test3204 test3205 test3207 test3208 test3209 \
+test3210 \
+\
EXTRA_DIST = $(TESTCASES) DISABLED
--- /dev/null
+<testcase>
+<info>
+<keywords>
+IMAP
+Clear Text
+APPEND
+UPLOAD
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+<name>
+Upload message via IMAP with upload flags
+</name>
+<command>
+imap://%HOSTIP:%IMAPPORT/%TESTNUMBER -T %LOGDIR/upload%TESTNUMBER -u user:secret --upload-flags answered,deleted,draft,flagged,seen
+</command>
+<file name="%LOGDIR/upload%TESTNUMBER">
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Hello Joe, do you think we can meet at 3:30 tomorrow?
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="yes">
+A001 CAPABILITY
+A002 LOGIN user secret
+A003 APPEND %TESTNUMBER (\Answered \Deleted \Draft \Flagged \Seen) {286}
+A004 LOGOUT
+</protocol>
+<upload>
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Hello Joe, do you think we can meet at 3:30 tomorrow?
+</upload>
+</verify>
+</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+IMAP
+Clear Text
+APPEND
+UPLOAD
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+<name>
+Upload message unread via IMAP
+</name>
+<command>
+imap://%HOSTIP:%IMAPPORT/%TESTNUMBER -T %LOGDIR/upload%TESTNUMBER -u user:secret --upload-flags -seen
+</command>
+<file name="%LOGDIR/upload%TESTNUMBER">
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</file>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol crlf="yes">
+A001 CAPABILITY
+A002 LOGIN user secret
+A003 APPEND %TESTNUMBER {356}
+A004 LOGOUT
+</protocol>
+<upload>
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+From: Fred Foobar <foobar@example.COM>
+Subject: afternoon meeting
+To: joe@example.com
+Message-Id: <B27397-0100000@example.COM>
+MIME-Version: 1.0
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</upload>
+</verify>
+</testcase>