1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*
 * mCaptcha - A proof of work based DoS protection system
 * Copyright © 2021 Aravinth Manivannan <realravinth@batsense.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

//! Errors and Result module
use derive_more::{Display, Error};
#[cfg(feature = "full")]
use redis::RedisError;
#[cfg(feature = "full")]
use tokio::sync::oneshot::error::RecvError;

/// Error datatype
#[derive(Debug, PartialEq, Display, Error)]
#[cfg(not(tarpaulin_include))]
pub enum CaptchaError {
    /// When configuring libmcaptcha, [DefenseBuilder][crate::defense::DefenseBuilder]
    /// must be passed atleast one `LevelConfig` if not this error will arise
    #[display(fmt = "LevelBuilder should have atleaset one level configured")]
    LevelEmpty,

    /// Visitor count must be a whole number(zero and above).
    /// When configuring libmcaptcha, [LevelBuilder][crate::defense::LevelBuilder].
    /// difficulty_factor must be set to greater than zero.
    #[display(fmt = "difficulty factor must be greater than zero")]
    DifficultyFactorZero,

    /// captcha cooldown duration must be greater than 0
    #[display(fmt = "difficulty factor must be greater than zero")]
    CaptchaDurationZero,

    /// Difficulty factor must be set
    #[display(fmt = "Set difficulty factor")]
    SetDifficultyFactor,

    /// Visitor threshold must be set
    #[display(fmt = "Set visitor threshold")]
    SetVisitorThreshold,

    /// Visitor count must be Unique
    #[display(fmt = "Duplicate visitor count")]
    DuplicateVisitorCount,

    /// Difficulty factor should increase with level
    #[display(fmt = "Difficulty factor should increase with level")]
    DecreaseingDifficultyFactor,

    /// Difficulty factor should increase with level
    #[display(fmt = "Actor mailbox error")]
    MailboxError,

    /// Happens when submitted work doesn't satisfy the required
    /// difficulty factor
    #[display(fmt = "Insuffiencient Difficulty")]
    InsuffiencientDifficulty,

    /// Happens when submitted work is computed over string that
    /// isn't in cache
    #[display(fmt = "String now found")]
    StringNotFound,

    /// Happens when submitted work is computed over configuration intended for
    /// a different mCAptcha sitekey
    #[display(fmt = "PoW computed over configuration not intended for target sitekey")]
    MCaptchaKeyValidationFail,

    /// Submitted PoW is invalid
    #[display(fmt = "Invalid PoW")]
    InvalidPoW,

    /// Used in builder structs when a value is not set
    #[display(fmt = "Please set value: {}", _0)]
    PleaseSetValue(#[error(not(source))] String),

    /// RedisError
    #[display(fmt = "{}", _0)]
    #[cfg(feature = "full")]
    RedisError(RedisError),

    /// Channel receive error
    #[display(fmt = "{}", _0)]
    #[cfg(feature = "full")]
    RecvError(RecvError),

    /// Weird behaviour from mcaptcha redis module
    #[display(
        fmt = "Something weird happening with mCaptcha redis module. Please file bug report"
    )]
    MCaptchaRedisModuleError,

    /// When libmcaptcha is ordered to connect to a Redis instance that doesn't have mCaptcha
    /// Redis module loaded
    #[display(
        fmt = "You are trying to connect to a Redis instance that doesn't have mCaptcha redis module loaded.
        Please see https://github.com/mCaptcha/cache for details on how to install mCaptcha redis module moudle"
    )]
    MCaptchaRedisModuleIsNotLoaded,

    /// MCaptcha redis module is loaded but it doesn't have the necessary Redis commands.
    /// Usually a version mismatch
    #[display(
        fmt = "The Redis instance that libmcaptcha is trying to connect to has mCaptcha Redis module loaded,
        but it's probably outdated and as a result, we are not able to find all required commands to operate mCaptcha
        Command {} is not found",
        _0
    )]
    MCaptchaRediSModuleCommandNotFound(#[error(not(source))] String),

    /// IP Queue is full
    QueueFull,
}

#[cfg(feature = "full")]
#[cfg(not(tarpaulin_include))]
impl From<RedisError> for CaptchaError {
    fn from(e: RedisError) -> Self {
        Self::RedisError(e)
    }
}

#[cfg(feature = "full")]
#[cfg(not(tarpaulin_include))]
impl From<RecvError> for CaptchaError {
    fn from(e: RecvError) -> Self {
        log::error!("{:?}", e);
        Self::RecvError(e)
    }
}

#[cfg(feature = "full")]
#[cfg(not(tarpaulin_include))]
impl From<actix::MailboxError> for CaptchaError {
    fn from(_: actix::MailboxError) -> Self {
        Self::MailboxError
    }
}

/// [Result] datatype for libmcaptcha
pub type CaptchaResult<V> = std::result::Result<V, CaptchaError>;