diff options
Diffstat (limited to 'src/lib-imap-storage/imap-msgpart-url.c')
-rw-r--r-- | src/lib-imap-storage/imap-msgpart-url.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/lib-imap-storage/imap-msgpart-url.c b/src/lib-imap-storage/imap-msgpart-url.c new file mode 100644 index 0000000..2469dd3 --- /dev/null +++ b/src/lib-imap-storage/imap-msgpart-url.c @@ -0,0 +1,287 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "net.h" +#include "istream.h" +#include "message-parser.h" +#include "mail-storage.h" +#include "mail-namespace.h" +#include "imap-url.h" +#include "imap-msgpart.h" +#include "imap-msgpart-url.h" + +struct imap_msgpart_url { + char *mailbox; + uint32_t uidvalidity; + uint32_t uid; + char *section; + uoff_t partial_offset, partial_size; + + struct imap_msgpart *part; + + struct mail_user *user; + struct mailbox *selected_box; + struct mailbox *box; + struct mailbox_transaction_context *trans; + struct mail *mail; + + struct imap_msgpart_open_result result; + + bool decode_cte_to_binary:1; +}; + +int imap_msgpart_url_create(struct mail_user *user, const struct imap_url *url, + struct imap_msgpart_url **mpurl_r, + const char **client_error_r) +{ + const char *section = url->section == NULL ? "" : url->section; + struct imap_msgpart_url *mpurl; + struct imap_msgpart *msgpart; + + if (url->mailbox == NULL || url->uid == 0 || + url->search_program != NULL) { + *client_error_r = "Invalid messagepart IMAP URL"; + return -1; + } + if (imap_msgpart_parse(section, &msgpart) < 0) { + *client_error_r = "Invalid section"; + return -1; + } + + mpurl = i_new(struct imap_msgpart_url, 1); + mpurl->part = msgpart; + mpurl->user = user; + mpurl->mailbox = i_strdup(url->mailbox); + mpurl->uidvalidity = url->uidvalidity; + mpurl->uid = url->uid; + if (url->section != NULL) + mpurl->section = i_strdup(url->section); + mpurl->partial_offset = url->partial_offset; + mpurl->partial_size = url->partial_size; + + imap_msgpart_set_partial(msgpart, url->partial_offset, + url->partial_size == 0 ? + UOFF_T_MAX : url->partial_size); + + *mpurl_r = mpurl; + return 0; +} + +int imap_msgpart_url_parse(struct mail_user *user, struct mailbox *selected_box, + const char *urlstr, struct imap_msgpart_url **mpurl_r, + const char **client_error_r) +{ + struct mailbox_status box_status; + struct imap_url base_url, *url; + const char *error; + + /* build base url */ + i_zero(&base_url); + if (selected_box != NULL) { + mailbox_get_open_status(selected_box, STATUS_UIDVALIDITY, + &box_status); + base_url.mailbox = mailbox_get_vname(selected_box); + base_url.uidvalidity = box_status.uidvalidity; + } + + /* parse url */ + if (imap_url_parse(urlstr, &base_url, + IMAP_URL_PARSE_REQUIRE_RELATIVE, &url, &error) < 0) { + *client_error_r = t_strconcat("Invalid IMAP URL: ", error, NULL); + return 0; + } + if (url->mailbox == NULL) { + *client_error_r = "Mailbox-relative IMAP URL, but no mailbox selected"; + return 0; + } + if (imap_msgpart_url_create(user, url, mpurl_r, client_error_r) < 0) + return -1; + (*mpurl_r)->selected_box = selected_box; + return 1; +} + +struct mailbox *imap_msgpart_url_get_mailbox(struct imap_msgpart_url *mpurl) +{ + return mpurl->box; +} + +int imap_msgpart_url_open_mailbox(struct imap_msgpart_url *mpurl, + struct mailbox **box_r, enum mail_error *error_code_r, + const char **client_error_r) +{ + struct mailbox_status box_status; + enum mailbox_flags flags = MAILBOX_FLAG_READONLY; + struct mail_namespace *ns; + struct mailbox *box; + + if (mpurl->box != NULL) { + *box_r = mpurl->box; + *error_code_r = MAIL_ERROR_NONE; + return 1; + } + + /* find mailbox namespace */ + ns = mail_namespace_find(mpurl->user->namespaces, mpurl->mailbox); + + /* open mailbox */ + if (mpurl->selected_box != NULL && + mailbox_equals(mpurl->selected_box, ns, mpurl->mailbox)) + box = mpurl->selected_box; + else + box = mailbox_alloc(ns->list, mpurl->mailbox, flags); + if (mailbox_open(box) < 0) { + *client_error_r = mail_storage_get_last_error(mailbox_get_storage(box), + error_code_r); + if (box != mpurl->selected_box) + mailbox_free(&box); + return *error_code_r == MAIL_ERROR_TEMP ? -1 : 0; + } + + /* verify UIDVALIDITY */ + mailbox_get_open_status(box, STATUS_UIDVALIDITY, &box_status); + if (mpurl->uidvalidity > 0 && + box_status.uidvalidity != mpurl->uidvalidity) { + *client_error_r = "Invalid UIDVALIDITY"; + *error_code_r = MAIL_ERROR_EXPUNGED; + if (box != mpurl->selected_box) + mailbox_free(&box); + return 0; + } + mpurl->box = box; + *box_r = box; + return 1; +} + +int imap_msgpart_url_open_mail(struct imap_msgpart_url *mpurl, + struct mail **mail_r, + const char **client_error_r) +{ + struct mailbox_transaction_context *t; + struct mailbox *box; + enum mail_error error_code; + struct mail *mail; + int ret; + + if (mpurl->mail != NULL) { + *mail_r = mpurl->mail; + return 1; + } + + /* open mailbox if it is not yet open */ + if ((ret = imap_msgpart_url_open_mailbox(mpurl, &box, &error_code, + client_error_r)) <= 0) + return ret; + + /* start transaction */ + t = mailbox_transaction_begin(box, 0, __func__); + mail = mail_alloc(t, MAIL_FETCH_MESSAGE_PARTS | + MAIL_FETCH_IMAP_BODYSTRUCTURE, NULL); + + /* find the message */ + if (!mail_set_uid(mail, mpurl->uid)) { + *client_error_r = "Message not found"; + mail_free(&mail); + mailbox_transaction_rollback(&t); + return 0; + } + + mpurl->trans = t; + mpurl->mail = mail; + *mail_r = mail; + return 1; +} + +struct imap_msgpart * +imap_msgpart_url_get_part(struct imap_msgpart_url *mpurl) +{ + return mpurl->part; +} + +void imap_msgpart_url_set_decode_to_binary(struct imap_msgpart_url *mpurl) +{ + imap_msgpart_set_decode_to_binary(mpurl->part); +} + +int imap_msgpart_url_read_part(struct imap_msgpart_url *mpurl, + struct imap_msgpart_open_result *result_r, + const char **client_error_r) +{ + struct mail *mail; + int ret; + + if (mpurl->result.input != NULL) { + i_stream_seek(mpurl->result.input, 0); + *result_r = mpurl->result; + return 1; + } + + /* open mail if it is not yet open */ + ret = imap_msgpart_url_open_mail(mpurl, &mail, client_error_r); + if (ret <= 0) + return ret; + + /* open the referenced part as a stream */ + ret = imap_msgpart_open(mail, mpurl->part, result_r); + if (ret < 0) { + *client_error_r = mailbox_get_last_error(mpurl->box, NULL); + return ret; + } + + mpurl->result = *result_r; + return 1; +} + +int imap_msgpart_url_verify(struct imap_msgpart_url *mpurl, + const char **client_error_r) +{ + struct mail *mail; + int ret; + + if (mpurl->result.input != NULL) + return 1; + + /* open mail if it is not yet open */ + ret = imap_msgpart_url_open_mail(mpurl, &mail, client_error_r); + return ret; +} + +int imap_msgpart_url_get_bodypartstructure(struct imap_msgpart_url *mpurl, + const char **bpstruct_r, + const char **client_error_r) +{ + struct mail *mail; + int ret; + + /* open mail if it is not yet open */ + ret = imap_msgpart_url_open_mail(mpurl, &mail, client_error_r); + if (ret <= 0) + return ret; + + ret = imap_msgpart_bodypartstructure(mail, mpurl->part, bpstruct_r); + if (ret < 0) + *client_error_r = mailbox_get_last_error(mpurl->box, NULL); + else if (ret == 0) + *client_error_r = "Message part not found"; + return ret; +} + +void imap_msgpart_url_free(struct imap_msgpart_url **_mpurl) +{ + struct imap_msgpart_url *mpurl = *_mpurl; + + *_mpurl = NULL; + + i_stream_unref(&mpurl->result.input); + if (mpurl->part != NULL) + imap_msgpart_free(&mpurl->part); + if (mpurl->mail != NULL) + mail_free(&mpurl->mail); + if (mpurl->trans != NULL) + mailbox_transaction_rollback(&mpurl->trans); + if (mpurl->box != NULL && mpurl->box != mpurl->selected_box) + mailbox_free(&mpurl->box); + if (mpurl->section != NULL) + i_free(mpurl->section); + i_free(mpurl->mailbox); + i_free(mpurl); +} |