Skip to content

Commit

Permalink
IMAP: add CURLOPT_UPLOAD_FLAGS and --upload-flags
Browse files Browse the repository at this point in the history
Set properties on the uploaded resource.

Test 3209 and 3210 verify.

Closes curl#15970
  • Loading branch information
tiymat authored and bagder committed Mar 4, 2025
1 parent 0cd2670 commit 6758aa7
Show file tree
Hide file tree
Showing 25 changed files with 395 additions and 16 deletions.
7 changes: 0 additions & 7 deletions docs/TODO
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@

9. IMAP
9.1 Enhanced capability support
9.2 upload unread

10. LDAP
10.1 SASL based authentication mechanisms
Expand Down Expand Up @@ -721,12 +720,6 @@
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
Expand Down
1 change: 1 addition & 0 deletions docs/cmdline-opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ DPAGES = \
trace.md \
unix-socket.md \
upload-file.md \
upload-flags.md \
url.md \
url-query.md \
use-ascii.md \
Expand Down
2 changes: 1 addition & 1 deletion docs/cmdline-opts/upload-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ filename or curl thinks that your last directory name is the remote filename
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.
Expand Down
24 changes: 24 additions & 0 deletions docs/cmdline-opts/upload-flags.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
c: Copyright (C) Daniel Stenberg, <[email protected]>, 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.
2 changes: 1 addition & 1 deletion docs/internals/STRPARSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ int Curl_str_quotedword(char **linep, struct Curl_str *out, const size_t max);
~~~

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.

Expand Down
4 changes: 4 additions & 0 deletions docs/libcurl/curl_easy_setopt.md
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,10 @@ Upload data. See CURLOPT_UPLOAD(3)
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)
Expand Down
100 changes: 100 additions & 0 deletions docs/libcurl/opts/CURLOPT_UPLOAD_FLAGS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
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, "[email protected]");
/* 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).
1 change: 1 addition & 0 deletions docs/libcurl/opts/Makefile.inc
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ man_MANS = \
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 \
Expand Down
6 changes: 6 additions & 0 deletions docs/libcurl/symbols-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ CURLOPT_UNRESTRICTED_AUTH 7.10.4
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
Expand Down Expand Up @@ -1128,6 +1129,11 @@ CURLUPART_SCHEME 7.62.0
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
Expand Down
1 change: 1 addition & 0 deletions docs/options-in-versions
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
--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
Expand Down
8 changes: 8 additions & 0 deletions include/curl/curl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,12 @@ typedef enum {
#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;
Expand Down Expand Up @@ -2237,6 +2243,8 @@ typedef enum {
/* 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;

Expand Down
3 changes: 2 additions & 1 deletion lib/easyoptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ struct curl_easyoption Curl_easyopts[] = {
{"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},
Expand All @@ -377,6 +378,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
return (CURLOPT_LASTENTRY % 10000) != (326 + 1);
return (CURLOPT_LASTENTRY % 10000) != (327 + 1);
}
#endif
45 changes: 42 additions & 3 deletions lib/imap.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
***************************************************************************/

#include "curl_setup.h"
#include "dynbuf.h"

#ifndef CURL_DISABLE_IMAP

Expand Down Expand Up @@ -192,6 +193,10 @@ static const struct SASLproto saslimap = {
SASL_FLAG_BASE64 /* Configuration flags */
};

struct ulbits {
int bit;
const char *flag;
};

/***********************************************************************
*
Expand Down Expand Up @@ -760,6 +765,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
CURLcode result = CURLE_OK;
struct IMAP *imap = data->req.p.imap;
char *mailbox;
struct dynbuf flags;

/* Check we have a mailbox */
if(!imap->mailbox) {
Expand Down Expand Up @@ -808,10 +814,43 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
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)
Expand Down
4 changes: 3 additions & 1 deletion lib/setopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1403,7 +1403,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
*/
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;
Expand Down
1 change: 1 addition & 0 deletions lib/urldata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1723,6 +1723,7 @@ struct UserDefined {
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 */
Expand Down
1 change: 1 addition & 0 deletions src/tool_cfgable.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ void config_init(struct OperationConfig *config)
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);
}

Expand Down
1 change: 1 addition & 0 deletions src/tool_cfgable.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ struct OperationConfig {
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? */
Expand Down
Loading

0 comments on commit 6758aa7

Please sign in to comment.