-
-
Notifications
You must be signed in to change notification settings - Fork 6k
/
RegistrationSessionProcessor.kt
182 lines (145 loc) · 6.56 KB
/
RegistrationSessionProcessor.kt
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package org.thoughtcrime.securesms.registration
import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException
import org.whispersystems.signalservice.api.push.exceptions.ExternalServiceFailureException
import org.whispersystems.signalservice.api.push.exceptions.ImpossiblePhoneNumberException
import org.whispersystems.signalservice.api.push.exceptions.InvalidTransportModeException
import org.whispersystems.signalservice.api.push.exceptions.MustRequestNewCodeException
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
import org.whispersystems.signalservice.api.push.exceptions.TokenNotAcceptedException
import org.whispersystems.signalservice.api.util.Preconditions
import org.whispersystems.signalservice.internal.ServiceResponse
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
import org.whispersystems.signalservice.internal.push.RegistrationSessionMetadataResponse
import kotlin.time.Duration.Companion.seconds
/**
* Makes the server's response describing the state of the registration session as digestible as possible.
*/
sealed class RegistrationSessionProcessor(response: ServiceResponse<RegistrationSessionMetadataResponse>) : ServiceResponseProcessor<RegistrationSessionMetadataResponse>(response) {
companion object {
const val CAPTCHA_KEY = "captcha"
const val PUSH_CHALLENGE_KEY = "pushChallenge"
val REQUESTABLE_INFORMATION = listOf(PUSH_CHALLENGE_KEY, CAPTCHA_KEY)
}
public override fun captchaRequired(): Boolean {
return super.captchaRequired() || (hasResult() && CAPTCHA_KEY == getChallenge())
}
public override fun rateLimit(): Boolean {
return error is RateLimitException
}
public override fun getError(): Throwable? {
return super.getError()
}
fun pushChallengeRequired(): Boolean {
return PUSH_CHALLENGE_KEY == getChallenge()
}
fun isTokenRejected(): Boolean {
return error is TokenNotAcceptedException
}
fun isImpossibleNumber(): Boolean {
return error is ImpossiblePhoneNumberException
}
fun isNonNormalizedNumber(): Boolean {
return error is NonNormalizedPhoneNumberException
}
fun getRateLimit(): Long {
Preconditions.checkState(error is RateLimitException, "This can only be called when isRateLimited()")
return (error as RateLimitException).retryAfterMilliseconds.orElse(-1L)
}
/**
* The soonest time at which the server will accept a request to send a new code via SMS.
* @return a unix timestamp in milliseconds, or 0 to represent null
*/
fun getNextCodeViaSmsAttempt(): Long {
return deriveTimestamp(result.body.nextSms)
}
/**
* The soonest time at which the server will accept a request to send a new code via a voice call.
* @return a unix timestamp in milliseconds, or 0 to represent null
*/
fun getNextCodeViaCallAttempt(): Long {
return deriveTimestamp(result.body.nextCall)
}
fun canSubmitProofImmediately(): Boolean {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
return 0 == result.body.nextVerificationAttempt
}
/**
* The soonest time at which the server will accept a submission of proof of ownership.
* @return a unix timestamp in milliseconds, or 0 to represent null
*/
fun getNextProofSubmissionAttempt(): Long {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
return deriveTimestamp(result.body.nextVerificationAttempt)
}
fun exhaustedVerificationCodeAttempts(): Boolean {
return rateLimit() && getRateLimit() == -1L
}
fun isInvalidSession(): Boolean {
return error is NoSuchSessionException
}
fun getSessionId(): String {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
return result.body.id
}
fun isAllowedToRequestCode(): Boolean {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
return result.body.allowedToRequestCode
}
fun getChallenge(): String? {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
return result.body.requestedInformation.firstOrNull { REQUESTABLE_INFORMATION.contains(it) }
}
fun isVerified(): Boolean {
return hasResult() && result.body.verified
}
/** Should only be called if [isNonNormalizedNumber] */
fun getOriginalNumber(): String {
if (error !is NonNormalizedPhoneNumberException) {
throw IllegalStateException("This can only be called when isNonNormalizedNumber()")
}
return (error as NonNormalizedPhoneNumberException).originalNumber
}
/** Should only be called if [isNonNormalizedNumber] */
fun getNormalizedNumber(): String {
if (error !is NonNormalizedPhoneNumberException) {
throw IllegalStateException("This can only be called when isNonNormalizedNumber()")
}
return (error as NonNormalizedPhoneNumberException).normalizedNumber
}
fun cannotSubmitVerificationAttempt(): Boolean {
return !hasResult() || result.body.nextVerificationAttempt == null
}
/**
* @param deltaSeconds the number of whole seconds to be added to the server timestamp
* @return a unix timestamp in milliseconds, or 0 to represent null
*/
private fun deriveTimestamp(deltaSeconds: Int?): Long {
Preconditions.checkState(hasResult(), "This can only be called when result is present!")
if (deltaSeconds == null) {
return 0L
}
val timestamp: Long = result.headers.timestamp
return timestamp + deltaSeconds.seconds.inWholeMilliseconds
}
abstract fun verificationCodeRequestSuccess(): Boolean
class RegistrationSessionProcessorForSession(response: ServiceResponse<RegistrationSessionMetadataResponse>) : RegistrationSessionProcessor(response) {
override fun verificationCodeRequestSuccess(): Boolean = false
}
class RegistrationSessionProcessorForVerification(response: ServiceResponse<RegistrationSessionMetadataResponse>) : RegistrationSessionProcessor(response) {
override fun verificationCodeRequestSuccess(): Boolean = hasResult()
fun isAlreadyVerified(): Boolean {
return error is AlreadyVerifiedException
}
fun mustRequestNewCode(): Boolean {
return error is MustRequestNewCodeException
}
fun externalServiceFailure(): Boolean {
return error is ExternalServiceFailureException
}
fun invalidTransportModeFailure(): Boolean {
return error is InvalidTransportModeException
}
}
}