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: [`PostFilter`](message_filters::PostFilter),
21
//! [`ListContext`], [`MailJob`] and [`PostAction`].
22

            
23
use melib::Address;
24

            
25
use super::*;
26
pub mod message_filters;
27

            
28
/// Post action returned from a list's
29
/// [`PostFilter`](message_filters::PostFilter) stack.
30
9
#[derive(Debug)]
31
pub enum PostAction {
32
    /// Add to `hold` queue.
33
    Hold,
34
    /// Accept to mailing list.
35
    Accept,
36
    /// Reject and send rejection response to submitter.
37
    Reject {
38
        /// Human readable reason for rejection.
39
3
        reason: String,
40
    },
41
    /// Add to `deferred` queue.
42
    Defer {
43
        /// Human readable reason for deferring.
44
        reason: String,
45
    },
46
}
47

            
48
/// List context passed to a list's [`PostFilter`](message_filters::PostFilter)
49
/// stack.
50
6
#[derive(Debug)]
51
pub struct ListContext<'list> {
52
    /// Which mailing list a post was addressed to.
53
3
    pub list: &'list MailingList,
54
    /// The mailing list owners.
55
3
    pub list_owners: &'list [DbVal<ListOwner>],
56
    /// The mailing list subscriptions.
57
3
    pub subscriptions: &'list [DbVal<ListSubscription>],
58
    /// The mailing list post policy.
59
    pub post_policy: Option<DbVal<PostPolicy>>,
60
    /// The mailing list subscription policy.
61
3
    pub subscription_policy: Option<DbVal<SubscriptionPolicy>>,
62
    /// The scheduled jobs added by each filter in a list's
63
    /// [`PostFilter`](message_filters::PostFilter) stack.
64
3
    pub scheduled_jobs: Vec<MailJob>,
65
}
66

            
67
/// Post to be considered by the list's
68
/// [`PostFilter`](message_filters::PostFilter) stack.
69
pub struct Post {
70
    /// `From` address of post.
71
    pub from: Address,
72
    /// Raw bytes of post.
73
    pub bytes: Vec<u8>,
74
    /// `To` addresses of post.
75
    pub to: Vec<Address>,
76
    /// Final action set by each filter in a list's
77
    /// [`PostFilter`](message_filters::PostFilter) stack.
78
    pub action: PostAction,
79
}
80

            
81
impl core::fmt::Debug for Post {
82
3
    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
83
12
        fmt.debug_struct("Post")
84
3
            .field("from", &self.from)
85
3
            .field("bytes", &format_args!("{} bytes", self.bytes.len()))
86
3
            .field("to", &self.to.as_slice())
87
            .field("action", &self.action)
88
            .finish()
89
3
    }
90
}
91

            
92
/// Scheduled jobs added to a [`ListContext`] by a list's
93
/// [`PostFilter`](message_filters::PostFilter) stack.
94
6
#[derive(Debug)]
95
pub enum MailJob {
96
    /// Send post to recipients.
97
    Send {
98
        /// The post recipients addresses.
99
6
        recipients: Vec<Address>,
100
    },
101
    /// Send error to submitter.
102
    Error {
103
        /// Human readable description of the error.
104
        description: String,
105
    },
106
    /// Store post in digest for recipients.
107
    StoreDigest {
108
        /// The digest recipients addresses.
109
        recipients: Vec<Address>,
110
    },
111
    /// Reply with subscription confirmation to submitter.
112
    ConfirmSubscription {
113
        /// The submitter address.
114
        recipient: Address,
115
    },
116
    /// Reply with unsubscription confirmation to submitter.
117
    ConfirmUnsubscription {
118
        /// The submitter address.
119
        recipient: Address,
120
    },
121
}
122

            
123
/// Type of mailing list request.
124
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
125
pub enum ListRequest {
126
    /// Get help about a mailing list and its available interfaces.
127
    Help,
128
    /// Request subscription.
129
    Subscribe,
130
    /// Request removal of subscription.
131
    Unsubscribe,
132
    /// Request reception of list posts from a month-year range, inclusive.
133
    RetrieveArchive(String, String),
134
    /// Request reception of specific mailing list posts from `Message-ID`
135
    /// values.
136
    RetrieveMessages(Vec<String>),
137
    /// Request change in subscription settings.
138
    /// See [`ListSubscription`].
139
    ChangeSetting(String, bool),
140
    /// Other type of request.
141
2
    Other(String),
142
}
143

            
144
impl std::fmt::Display for ListRequest {
145
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
146
        write!(fmt, "{:?}", self)
147
    }
148
}
149

            
150
impl<S: AsRef<str>> TryFrom<(S, &melib::Envelope)> for ListRequest {
151
    type Error = crate::Error;
152

            
153
12
    fn try_from((val, env): (S, &melib::Envelope)) -> std::result::Result<Self, Self::Error> {
154
12
        let val = val.as_ref();
155
12
        Ok(match val {
156
12
            "subscribe" => Self::Subscribe,
157
6
            "request" if env.subject().trim() == "subscribe" => Self::Subscribe,
158
4
            "unsubscribe" => Self::Unsubscribe,
159
4
            "request" if env.subject().trim() == "unsubscribe" => Self::Unsubscribe,
160
3
            "help" => Self::Help,
161
3
            "request" if env.subject().trim() == "help" => Self::Help,
162
2
            "request" => Self::Other(env.subject().trim().to_string()),
163
            _ => {
164
                // [ref:TODO] add ChangeSetting parsing
165
                trace!("unknown action = {} for addresses {:?}", val, env.from(),);
166
                Self::Other(val.trim().to_string())
167
            }
168
        })
169
12
    }
170
}