1
/*
2
 * This file is part of mailpot
3
 *
4
 * Copyright 2020 - Manos Pitsidianakis
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as
8
 * published by the Free Software Foundation, either version 3 of the
9
 * License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 */
19

            
20
//! Types for processing new posts:
21
//! [`PostFilter`](crate::message_filters::PostFilter), [`ListContext`],
22
//! [`MailJob`] and [`PostAction`].
23

            
24
use std::collections::HashMap;
25

            
26
use log::trace;
27
use melib::{Address, MessageID};
28

            
29
use crate::{
30
    models::{ListOwner, ListSubscription, MailingList, PostPolicy, SubscriptionPolicy},
31
    DbVal,
32
};
33
/// Post action returned from a list's
34
/// [`PostFilter`](crate::message_filters::PostFilter) stack.
35
15
#[derive(Debug)]
36
pub enum PostAction {
37
    /// Add to `hold` queue.
38
    Hold,
39
    /// Accept to mailing list.
40
    Accept,
41
    /// Reject and send rejection response to submitter.
42
    Reject {
43
        /// Human readable reason for rejection.
44
4
        reason: String,
45
    },
46
    /// Add to `deferred` queue.
47
    Defer {
48
        /// Human readable reason for deferring.
49
1
        reason: String,
50
    },
51
}
52

            
53
/// List context passed to a list's
54
/// [`PostFilter`](crate::message_filters::PostFilter) stack.
55
10
#[derive(Debug)]
56
pub struct ListContext<'list> {
57
    /// Which mailing list a post was addressed to.
58
5
    pub list: &'list MailingList,
59
    /// The mailing list owners.
60
5
    pub list_owners: &'list [DbVal<ListOwner>],
61
    /// The mailing list subscriptions.
62
5
    pub subscriptions: &'list [DbVal<ListSubscription>],
63
    /// The mailing list post policy.
64
5
    pub post_policy: Option<DbVal<PostPolicy>>,
65
    /// The mailing list subscription policy.
66
5
    pub subscription_policy: Option<DbVal<SubscriptionPolicy>>,
67
    /// The scheduled jobs added by each filter in a list's
68
    /// [`PostFilter`](crate::message_filters::PostFilter) stack.
69
    pub scheduled_jobs: Vec<MailJob>,
70
    /// Saved settings for message filters, which process a
71
    /// received e-mail before taking a final decision/action.
72
5
    pub filter_settings: HashMap<String, DbVal<serde_json::Value>>,
73
}
74

            
75
/// Post to be considered by the list's
76
/// [`PostFilter`](crate::message_filters::PostFilter) stack.
77
pub struct PostEntry {
78
    /// `From` address of post.
79
    pub from: Address,
80
    /// Raw bytes of post.
81
    pub bytes: Vec<u8>,
82
    /// `To` addresses of post.
83
    pub to: Vec<Address>,
84
    /// Final action set by each filter in a list's
85
    /// [`PostFilter`](crate::message_filters::PostFilter) stack.
86
    pub action: PostAction,
87
    /// Post's Message-ID
88
    pub message_id: MessageID,
89
}
90

            
91
impl core::fmt::Debug for PostEntry {
92
5
    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
93
25
        fmt.debug_struct(stringify!(PostEntry))
94
5
            .field("from", &self.from)
95
5
            .field("message_id", &self.message_id)
96
5
            .field("bytes", &format_args!("{} bytes", self.bytes.len()))
97
5
            .field("to", &self.to.as_slice())
98
            .field("action", &self.action)
99
            .finish()
100
5
    }
101
}
102

            
103
/// Scheduled jobs added to a [`ListContext`] by a list's
104
/// [`PostFilter`](crate::message_filters::PostFilter) stack.
105
10
#[derive(Debug)]
106
pub enum MailJob {
107
    /// Send post to recipients.
108
    Send {
109
        /// The post recipients addresses.
110
10
        recipients: Vec<Address>,
111
    },
112
    /// Send error to submitter.
113
    Error {
114
        /// Human readable description of the error.
115
        description: String,
116
    },
117
    /// Store post in digest for recipients.
118
    StoreDigest {
119
        /// The digest recipients addresses.
120
        recipients: Vec<Address>,
121
    },
122
    /// Reply with subscription confirmation to submitter.
123
    ConfirmSubscription {
124
        /// The submitter address.
125
        recipient: Address,
126
    },
127
    /// Reply with unsubscription confirmation to submitter.
128
    ConfirmUnsubscription {
129
        /// The submitter address.
130
        recipient: Address,
131
    },
132
}
133

            
134
/// Type of mailing list request.
135
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
136
pub enum ListRequest {
137
    /// Get help about a mailing list and its available interfaces.
138
    Help,
139
    /// Request subscription.
140
    Subscribe,
141
    /// Request removal of subscription.
142
    Unsubscribe,
143
    /// Request reception of list posts from a month-year range, inclusive.
144
    RetrieveArchive(String, String),
145
    /// Request reception of specific mailing list posts from `Message-ID`
146
    /// values.
147
    RetrieveMessages(Vec<String>),
148
    /// Request change in subscription settings.
149
    /// See [`ListSubscription`].
150
    ChangeSetting(String, bool),
151
    /// Other type of request.
152
    Other(String),
153
}
154

            
155
impl std::fmt::Display for ListRequest {
156
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
157
        write!(fmt, "{:?}", self)
158
    }
159
}
160

            
161
impl<S: AsRef<str>> TryFrom<(S, &melib::Envelope)> for ListRequest {
162
    type Error = crate::Error;
163

            
164
28
    fn try_from((val, env): (S, &melib::Envelope)) -> std::result::Result<Self, Self::Error> {
165
28
        let val = val.as_ref();
166
14
        Ok(match val {
167
14
            "subscribe" => Self::Subscribe,
168
7
            "request" if env.subject().trim() == "subscribe" => Self::Subscribe,
169
5
            "unsubscribe" => Self::Unsubscribe,
170
5
            "request" if env.subject().trim() == "unsubscribe" => Self::Unsubscribe,
171
3
            "help" => Self::Help,
172
3
            "request" if env.subject().trim() == "help" => Self::Help,
173
2
            "request" => Self::Other(env.subject().trim().to_string()),
174
            _ => {
175
                // [ref:TODO] add ChangeSetting parsing
176
                trace!("unknown action = {} for addresses {:?}", val, env.from(),);
177
                Self::Other(val.trim().to_string())
178
            }
179
        })
180
14
    }
181
}