001/*
002 *   Copyright 2024 Vonage
003 *
004 *   Licensed under the Apache License, Version 2.0 (the "License");
005 *   you may not use this file except in compliance with the License.
006 *   You may obtain a copy of the License at
007 *
008 *        http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *   Unless required by applicable law or agreed to in writing, software
011 *   distributed under the License is distributed on an "AS IS" BASIS,
012 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *   See the License for the specific language governing permissions and
014 *   limitations under the License.
015 */
016package com.vonage.client.video;
017
018import com.fasterxml.jackson.annotation.JsonIgnore;
019import com.fasterxml.jackson.annotation.JsonProperty;
020import com.vonage.client.JsonableBaseObject;
021import java.net.URI;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.Objects;
025
026/**
027 * Represents an outbound SIP dial request's properties.
028 */
029public class SipDialRequest extends AbstractSessionTokenRequest {
030        @JsonProperty("sip") private Sip sip;
031
032        static class Sip extends JsonableBaseObject {
033                @JsonProperty("uri") String uri;
034                @JsonProperty("from") String from;
035                @JsonProperty("headers") Map<String, String> headers;
036                @JsonProperty("auth") Auth auth;
037                @JsonProperty("secure") Boolean secure;
038                @JsonProperty("video") Boolean video;
039                @JsonProperty("observeForceMute") Boolean observeForceMute;
040
041                static class Auth extends JsonableBaseObject {
042                        @JsonProperty("username") String username;
043                        @JsonProperty("password") String password;
044                }
045        }
046
047        /**
048     * For tests.
049     */
050        SipDialRequest() {}
051
052        private SipDialRequest(Builder builder) {
053                super(builder);
054                sip = new Sip();
055                sip.uri = Objects.requireNonNull(builder.uri, "SIP URI is required.");
056                sip.from = builder.from;
057                sip.secure = builder.secure;
058                sip.video = builder.video;
059                sip.observeForceMute = builder.observeForceMute;
060                if (!builder.headers.isEmpty()) {
061                        sip.headers = builder.headers;
062                }
063                if (builder.username != null) {
064                        sip.auth = new Sip.Auth();
065                        sip.auth.username = builder.username;
066                        if (builder.password != null) {
067                                sip.auth.password = builder.password;
068                        }
069                }
070                else if (builder.password != null) {
071                        throw new IllegalStateException("SIP Auth username is required if password is provided.");
072                }
073        }
074
075        @JsonIgnore
076        public String getUri() {
077                return sip.uri;
078        }
079
080        @JsonIgnore
081        public String getFrom() {
082                return sip.from;
083        }
084
085        @JsonIgnore
086        public Map<String, String> getHeaders() {
087                return sip.headers;
088        }
089
090        @JsonIgnore
091        public String getUsername() {
092                return sip.auth != null ? sip.auth.username : null;
093        }
094
095        @JsonIgnore
096        public String getPassword() {
097                return sip.auth != null ? sip.auth.password : null;
098        }
099
100        @JsonIgnore
101        public Boolean getSecure() {
102                return sip.secure;
103        }
104
105        @JsonIgnore
106        public Boolean getVideo() {
107                return sip.video;
108        }
109
110        @JsonIgnore
111        public Boolean getObserveForceMute() {
112                return sip.observeForceMute;
113        }
114
115        /**
116         * Instantiates a Builder, used to construct this object.
117         *
118         * @return A new {@linkplain Builder}.
119         */
120        public static Builder builder() {
121                return new Builder();
122        }
123
124        /**
125         * Used to create an SipDialRequest object.
126         *
127         * @see SipDialRequest
128         */
129        public static class Builder extends AbstractSessionTokenRequest.Builder<SipDialRequest, Builder> {
130                private final Map<String, String> headers = new HashMap<>();
131                private String from, uri, username, password;
132                private Boolean secure, video, observeForceMute;
133
134                /**
135                 * (REQUIRED)
136                 * The SIP URI to be used as destination of the SIP call initiated from Vonage to your SIP platform.
137                 *
138                 * @param uri The URI as an object.
139                 *
140                 * @param secure Whether the negotiation between Vonage and the SIP endpoint will be done securely.
141                 * Note that this will only apply to the negotiation itself, and not to the transmission of audio.
142                 * Setting this to true will append {@code transport=tls} to the URI.
143                 * If you also want the audio transmission to be encrypted, set the {@link #secure(boolean)}
144                 * property of this builder to true.
145                 *
146                 * @return This builder.
147                 */
148                public Builder uri(URI uri, boolean secure) {
149                        this.uri = uri + (secure ? ";transport=tls" : "");
150                        return this;
151                }
152
153                /**
154                 * (OPTIONAL)
155                 * The number or string that will be sent to the final SIP number as the caller. It must be a string in
156                 * the form of {@code from@example.com}, where from can be a string or a number. If from is set to a number
157                 * (for example, "14155550101@example.com"), it will show up as the incoming number on PSTN phones. If from
158                 * is undefined or set to a string (for example, "joe@example.com"), +00000000 will show up as the incoming
159                 * number on PSTN phones.
160                 *
161                 * @param from The caller number or email as a string.
162                 *
163                 * @return This builder.
164                 */
165                public Builder from(String from) {
166                        this.from = from;
167                        return this;
168                }
169
170                /**
171                 * (OPTIONAL, but REQUIRED if {@link #password(String)} is provided)
172                 * The username to be used in the SIP INVITE request for HTTP digest authentication,
173                 * if it is required by your SIP platform.
174                 *
175                 * @param username The username as a string.
176                 *
177                 * @return This builder.
178                 * @see #password(String)
179                 */
180                public Builder username(String username) {
181                        this.username = username;
182                        return this;
183                }
184
185                /**
186                 * (OPTIONAL)
187                 * The password corresponding to the username for to be used in the SIP INVITE request.
188                 *
189                 * @param password The password as a string.
190                 *
191                 * @return This builder.
192                 * @see #username(String)
193                 */
194                public Builder password(String password) {
195                        this.password = password;
196                        return this;
197                }
198
199                /**
200                 * (OPTIONAL)
201                 * Define a custom header to be added to the SIP INVITE request initiated from Vonage to your SIP platform.
202                 *
203                 * @param key The header key.
204                 * @param value The header value.
205                 *
206                 * @return This builder.
207                 * @see #addHeaders(Map)
208                 */
209                public Builder addHeader(String key, String value) {
210                        headers.put(key, value);
211                        return this;
212                }
213
214                /**
215                 * (OPTIONAL)
216                 * Define custom headers (i.e. those starting with "X-") to be added to the SIP INVITE request
217                 * initiated from Vonage to your SIP platform.
218                 *
219                 * @param headers Custom header key-value pairs as strings.
220                 *
221                 * @return This builder.
222                 * @see #addHeader(String, String)
223                 */
224                public Builder addHeaders(Map<String, String> headers) {
225                        this.headers.putAll(headers);
226                        return this;
227                }
228
229                /**
230                 * (OPTIONAL)
231                 * Flag that indicates whether the media must be transmitted encrypted (true) or not (false, the default).
232                 *
233                 * @param secure Whether media should be transmitted securely.
234                 *
235                 * @return This builder.
236                 */
237                public Builder secure(boolean secure) {
238                        this.secure = secure;
239                        return this;
240                }
241
242                /**
243                 * (OPTIONAL)
244                 *  Flag that indicates whether the SIP call will include video (true) or not (false, the default).
245                 *  With video included, the SIP client's video is included in the video stream that is sent to the
246                 *  Vonage video session. The SIP client will receive a single composed video of the published streams
247                 *  in the Vonage video session.
248                 *
249                 * @param video Whether to include video in the SIP call.
250                 *
251                 * @return This builder.
252                 */
253                public Builder video(boolean video) {
254                        this.video = video;
255                        return this;
256                }
257
258                /**
259                 * (OPTIONAL)
260                 * flag that indicates whether the SIP end point observes force mute moderation (true) or not (false, the
261                 * default). Also, with observeForceMute set to true, the caller can press "*6" to unmute and mute the
262                 * published audio. For the "*6" mute toggle to work, the SIP caller must negotiate RFC2833 DTMFs
263                 * (RFC2833/RFC4733 digits). The mute toggle is not supported with SIP INFO or in-band DTMFs. A message
264                 * (in English) is played to the caller when the caller mutes and unmutes, or when the SIP client is muted
265                 * through a force mute action.
266                 *
267                 * @param observeForceMute Whether to observe forceMute moderation.
268                 *
269                 * @return This builder.
270                 */
271                public Builder observeForceMute(boolean observeForceMute) {
272                        this.observeForceMute = observeForceMute;
273                        return this;
274                }
275
276                /**
277                 * Builds the {@linkplain SipDialRequest} object with this builder's settings.
278                 *
279                 * @return A new {@link SipDialRequest} instance.
280                 */
281                @Override
282                public SipDialRequest build() {
283                        return new SipDialRequest(this);
284                }
285        }
286}