Line data Source code
1 : /*
2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc.
3 : *
4 : * This program is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 : */
17 : #include "sipaccount_config.h"
18 : #include "account_const.h"
19 : #include "account_schema.h"
20 : #include "config/yamlparser.h"
21 :
22 : extern "C" {
23 : #include <pjlib-util/md5.h>
24 : }
25 :
26 : namespace jami {
27 :
28 : namespace Conf {
29 : constexpr const char* ID_KEY = "id";
30 : constexpr const char* USERNAME_KEY = "username";
31 : constexpr const char* BIND_ADDRESS_KEY = "bindAddress";
32 : constexpr const char* INTERFACE_KEY = "interface";
33 : constexpr const char* PORT_KEY = "port";
34 : constexpr const char* PUBLISH_ADDR_KEY = "publishAddr";
35 : constexpr const char* PUBLISH_PORT_KEY = "publishPort";
36 : constexpr const char* SAME_AS_LOCAL_KEY = "sameasLocal";
37 : constexpr const char* DTMF_TYPE_KEY = "dtmfType";
38 : constexpr const char* SERVICE_ROUTE_KEY = "serviceRoute";
39 : constexpr const char* ALLOW_IP_AUTO_REWRITE = "allowIPAutoRewrite";
40 : constexpr const char* PRESENCE_PUBLISH_SUPPORTED_KEY = "presencePublishSupported";
41 : constexpr const char* PRESENCE_SUBSCRIBE_SUPPORTED_KEY = "presenceSubscribeSupported";
42 : constexpr const char* PRESENCE_STATUS_KEY = "presenceStatus";
43 : constexpr const char* PRESENCE_NOTE_KEY = "presenceNote";
44 : constexpr const char* PRESENCE_MODULE_ENABLED_KEY = "presenceModuleEnabled";
45 : constexpr const char* KEEP_ALIVE_ENABLED = "keepAliveEnabled";
46 :
47 : constexpr const char* const TLS_KEY = "tls";
48 : constexpr const char* CERTIFICATE_KEY = "certificate";
49 : constexpr const char* CALIST_KEY = "calist";
50 : constexpr const char* TLS_PORT_KEY = "tlsPort";
51 : constexpr const char* CIPHERS_KEY = "ciphers";
52 : constexpr const char* TLS_ENABLE_KEY = "enable";
53 : constexpr const char* METHOD_KEY = "method";
54 : constexpr const char* TIMEOUT_KEY = "timeout";
55 : constexpr const char* TLS_PASSWORD_KEY = "password";
56 : constexpr const char* PRIVATE_KEY_KEY = "privateKey";
57 : constexpr const char* REQUIRE_CERTIF_KEY = "requireCertif";
58 : constexpr const char* SERVER_KEY = "server";
59 : constexpr const char* VERIFY_CLIENT_KEY = "verifyClient";
60 : constexpr const char* VERIFY_SERVER_KEY = "verifyServer";
61 : constexpr const char* DISABLE_SECURE_DLG_CHECK = "disableSecureDlgCheck";
62 :
63 : constexpr const char* STUN_ENABLED_KEY = "stunEnabled";
64 : constexpr const char* STUN_SERVER_KEY = "stunServer";
65 : constexpr const char* CRED_KEY = "credential";
66 : constexpr const char* SRTP_KEY = "srtp";
67 : constexpr const char* KEY_EXCHANGE_KEY = "keyExchange";
68 : constexpr const char* RTP_FALLBACK_KEY = "rtpFallback";
69 : } // namespace Conf
70 :
71 : static const SipAccountConfig DEFAULT_CONFIG {};
72 : static constexpr unsigned MIN_REGISTRATION_TIME = 60; // seconds
73 :
74 : using yaml_utils::parseValueOptional;
75 : using yaml_utils::parseVectorMap;
76 :
77 : void
78 224 : SipAccountConfig::serialize(YAML::Emitter& out) const
79 : {
80 224 : out << YAML::BeginMap;
81 224 : out << YAML::Key << Conf::ID_KEY << YAML::Value << id;
82 224 : SipAccountBaseConfig::serializeDiff(out, DEFAULT_CONFIG);
83 :
84 224 : out << YAML::Key << Conf::BIND_ADDRESS_KEY << YAML::Value << bindAddress;
85 224 : out << YAML::Key << Conf::PORT_KEY << YAML::Value << localPort;
86 224 : out << YAML::Key << Conf::PUBLISH_PORT_KEY << YAML::Value << publishedPort;
87 :
88 224 : out << YAML::Key << Conf::USERNAME_KEY << YAML::Value << username;
89 :
90 : // each credential is a map, and we can have multiple credentials
91 224 : out << YAML::Key << Conf::CRED_KEY << YAML::Value << getCredentials();
92 :
93 224 : out << YAML::Key << Conf::KEEP_ALIVE_ENABLED << YAML::Value << registrationRefreshEnabled;
94 :
95 : // out << YAML::Key << PRESENCE_MODULE_ENABLED_KEY << YAML::Value
96 : // << (presence_ and presence_->isEnabled());
97 :
98 : out << YAML::Key << Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE << YAML::Value
99 224 : << registrationExpire;
100 224 : out << YAML::Key << Conf::SERVICE_ROUTE_KEY << YAML::Value << serviceRoute;
101 224 : out << YAML::Key << Conf::ALLOW_IP_AUTO_REWRITE << YAML::Value << allowIPAutoRewrite;
102 224 : out << YAML::Key << Conf::STUN_ENABLED_KEY << YAML::Value << stunEnabled;
103 224 : out << YAML::Key << Conf::STUN_SERVER_KEY << YAML::Value << stunServer;
104 :
105 : // tls submap
106 224 : out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap;
107 224 : out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile;
108 224 : out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile;
109 224 : out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword;
110 224 : out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile;
111 224 : out << YAML::Key << Conf::TLS_ENABLE_KEY << YAML::Value << tlsEnable;
112 224 : out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort;
113 224 : out << YAML::Key << Conf::VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient;
114 224 : out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer;
115 224 : out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate;
116 224 : out << YAML::Key << Conf::DISABLE_SECURE_DLG_CHECK << YAML::Value << tlsDisableSecureDlgCheck;
117 224 : out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeout;
118 224 : out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers;
119 224 : out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod;
120 224 : out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName;
121 224 : out << YAML::EndMap;
122 :
123 : // srtp submap
124 224 : out << YAML::Key << Conf::SRTP_KEY << YAML::Value << YAML::BeginMap;
125 : out << YAML::Key << Conf::KEY_EXCHANGE_KEY << YAML::Value
126 224 : << sip_utils::getKeyExchangeName(srtpKeyExchange);
127 224 : out << YAML::Key << Conf::RTP_FALLBACK_KEY << YAML::Value << srtpFallback;
128 224 : out << YAML::EndMap;
129 :
130 224 : out << YAML::EndMap;
131 224 : }
132 :
133 : void
134 0 : SipAccountConfig::unserialize(const YAML::Node& node)
135 : {
136 0 : SipAccountBaseConfig::unserialize(node);
137 0 : parseValueOptional(node, Conf::USERNAME_KEY, username);
138 0 : parseValueOptional(node, Conf::BIND_ADDRESS_KEY, bindAddress);
139 0 : parseValueOptional(node, Conf::PORT_KEY, localPort);
140 0 : parseValueOptional(node, Conf::PUBLISH_PORT_KEY, publishedPort);
141 0 : parseValueOptional(node, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
142 0 : registrationExpire = std::max(MIN_REGISTRATION_TIME, registrationExpire);
143 0 : parseValueOptional(node, Conf::KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
144 0 : parseValueOptional(node, Conf::SERVICE_ROUTE_KEY, serviceRoute);
145 0 : parseValueOptional(node, Conf::ALLOW_IP_AUTO_REWRITE, allowIPAutoRewrite);
146 :
147 0 : parseValueOptional(node, Conf::PRESENCE_MODULE_ENABLED_KEY, presenceEnabled);
148 0 : parseValueOptional(node, Conf::PRESENCE_PUBLISH_SUPPORTED_KEY, publishSupported);
149 0 : parseValueOptional(node, Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY, subscribeSupported);
150 :
151 : // ICE - STUN/TURN
152 0 : parseValueOptional(node, Conf::STUN_ENABLED_KEY, stunEnabled);
153 0 : parseValueOptional(node, Conf::STUN_SERVER_KEY, stunServer);
154 :
155 0 : const auto& credsNode = node[Conf::CRED_KEY];
156 0 : setCredentials(parseVectorMap(credsNode,
157 : {Conf::CONFIG_ACCOUNT_REALM,
158 : Conf::CONFIG_ACCOUNT_USERNAME,
159 : Conf::CONFIG_ACCOUNT_PASSWORD}));
160 :
161 : // get tls submap
162 : try {
163 0 : const auto& tlsMap = node[Conf::TLS_KEY];
164 0 : parseValueOptional(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile);
165 0 : parseValueOptional(tlsMap, Conf::CALIST_KEY, tlsCaListFile);
166 0 : parseValueOptional(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword);
167 0 : parseValueOptional(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile);
168 0 : parseValueOptional(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable);
169 0 : parseValueOptional(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort);
170 0 : parseValueOptional(tlsMap, Conf::CIPHERS_KEY, tlsCiphers);
171 0 : parseValueOptional(tlsMap, Conf::METHOD_KEY, tlsMethod);
172 0 : parseValueOptional(tlsMap, Conf::SERVER_KEY, tlsServerName);
173 0 : parseValueOptional(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate);
174 0 : parseValueOptional(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient);
175 0 : parseValueOptional(tlsMap, Conf::VERIFY_SERVER_KEY, tlsVerifyServer);
176 0 : parseValueOptional(tlsMap, Conf::DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
177 0 : parseValueOptional(tlsMap, Conf::TIMEOUT_KEY, tlsNegotiationTimeout);
178 0 : } catch (...) {
179 0 : }
180 :
181 : // get srtp submap
182 0 : const auto& srtpMap = node[Conf::SRTP_KEY];
183 0 : std::string tmpKey;
184 0 : parseValueOptional(srtpMap, Conf::KEY_EXCHANGE_KEY, tmpKey);
185 0 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(tmpKey);
186 0 : parseValueOptional(srtpMap, Conf::RTP_FALLBACK_KEY, srtpFallback);
187 0 : }
188 :
189 : std::map<std::string, std::string>
190 91 : SipAccountConfig::toMap() const
191 : {
192 91 : auto a = SipAccountBaseConfig::toMap();
193 : // general sip settings
194 91 : a.emplace(Conf::CONFIG_ACCOUNT_USERNAME, username);
195 91 : a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort));
196 91 : a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
197 91 : a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface);
198 91 : a.emplace(Conf::CONFIG_PUBLISHED_PORT, std::to_string(publishedPort));
199 91 : a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal ? TRUE_STR : FALSE_STR);
200 91 : a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
201 91 : a.emplace(Conf::CONFIG_STUN_ENABLE, stunEnabled ? TRUE_STR : FALSE_STR);
202 91 : a.emplace(Conf::CONFIG_STUN_SERVER, stunServer);
203 91 : a.emplace(Conf::CONFIG_BIND_ADDRESS, bindAddress);
204 91 : a.emplace(Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
205 91 : a.emplace(Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite ? TRUE_STR : FALSE_STR);
206 91 : a.emplace(Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled ? TRUE_STR : FALSE_STR);
207 91 : a.emplace(Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled ? TRUE_STR : FALSE_STR);
208 91 : a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, std::to_string(registrationExpire));
209 :
210 91 : std::string password {};
211 91 : if (not credentials.empty()) {
212 68 : for (const auto& cred : credentials)
213 68 : if (cred.username == username) {
214 68 : password = cred.password;
215 68 : break;
216 : }
217 : }
218 91 : a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password));
219 :
220 : // srtp settings
221 91 : a.emplace(Conf::CONFIG_SRTP_KEY_EXCHANGE, sip_utils::getKeyExchangeName(srtpKeyExchange));
222 91 : a.emplace(Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback ? TRUE_STR : FALSE_STR);
223 :
224 91 : a.emplace(Conf::CONFIG_TLS_ENABLE, tlsEnable ? TRUE_STR : FALSE_STR);
225 91 : a.emplace(Conf::CONFIG_TLS_LISTENER_PORT, std::to_string(tlsListenerPort));
226 91 : a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile);
227 91 : a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile);
228 91 : a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile);
229 91 : a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword);
230 91 : a.emplace(Conf::CONFIG_TLS_METHOD, tlsMethod);
231 91 : a.emplace(Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
232 91 : a.emplace(Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
233 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer ? TRUE_STR : FALSE_STR);
234 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient ? TRUE_STR : FALSE_STR);
235 91 : a.emplace(Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE,
236 91 : tlsRequireClientCertificate ? TRUE_STR : FALSE_STR);
237 91 : a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, std::to_string(tlsNegotiationTimeout));
238 91 : a.emplace(Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK,
239 91 : tlsDisableSecureDlgCheck ? TRUE_STR : FALSE_STR);
240 182 : return a;
241 91 : }
242 :
243 : void
244 24 : SipAccountConfig::fromMap(const std::map<std::string, std::string>& details)
245 : {
246 24 : SipAccountBaseConfig::fromMap(details);
247 :
248 : // general sip settings
249 24 : parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username);
250 24 : parseInt(details, Conf::CONFIG_LOCAL_PORT, localPort);
251 24 : parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress);
252 24 : parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
253 24 : parseBool(details, Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite);
254 24 : parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface);
255 24 : parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal);
256 24 : parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
257 24 : parseInt(details, Conf::CONFIG_PUBLISHED_PORT, publishedPort);
258 24 : parseBool(details, Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled);
259 24 : parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
260 24 : parseBool(details, Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
261 24 : parseInt(details, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
262 :
263 : // srtp settings
264 24 : parseBool(details, Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback);
265 24 : auto iter = details.find(Conf::CONFIG_SRTP_KEY_EXCHANGE);
266 24 : if (iter != details.end())
267 24 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(iter->second);
268 :
269 24 : if (credentials.empty()) { // credentials not set, construct 1 entry
270 24 : JAMI_WARN("No credentials set, inferring them...");
271 24 : std::map<std::string, std::string> map;
272 24 : map[Conf::CONFIG_ACCOUNT_USERNAME] = username;
273 24 : parseString(details, Conf::CONFIG_ACCOUNT_PASSWORD, map[Conf::CONFIG_ACCOUNT_PASSWORD]);
274 24 : map[Conf::CONFIG_ACCOUNT_REALM] = "*";
275 48 : setCredentials({map});
276 24 : }
277 :
278 : // ICE - STUN
279 24 : parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled);
280 24 : parseString(details, Conf::CONFIG_STUN_SERVER, stunServer);
281 :
282 : // TLS
283 24 : parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable);
284 24 : parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort);
285 24 : parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile, path);
286 24 : parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile, path);
287 24 : parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile, path);
288 24 : parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword);
289 24 : parseString(details, Conf::CONFIG_TLS_METHOD, tlsMethod);
290 24 : parseString(details, Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
291 24 : parseString(details, Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
292 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer);
293 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient);
294 24 : parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate);
295 24 : parseBool(details, Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
296 24 : parseInt(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeout);
297 24 : }
298 :
299 24 : SipAccountConfig::Credentials::Credentials(const std::map<std::string, std::string>& cred)
300 : {
301 24 : auto itrealm = cred.find(Conf::CONFIG_ACCOUNT_REALM);
302 24 : auto user = cred.find(Conf::CONFIG_ACCOUNT_USERNAME);
303 24 : auto passw = cred.find(Conf::CONFIG_ACCOUNT_PASSWORD);
304 24 : realm = itrealm != cred.end() ? itrealm->second : "";
305 24 : username = user != cred.end() ? user->second : "";
306 24 : password = passw != cred.end() ? passw->second : "";
307 24 : computePasswordHash();
308 24 : }
309 :
310 : std::map<std::string, std::string>
311 224 : SipAccountConfig::Credentials::toMap() const
312 : {
313 224 : return {{Conf::CONFIG_ACCOUNT_REALM, realm},
314 224 : {Conf::CONFIG_ACCOUNT_USERNAME, username},
315 896 : {Conf::CONFIG_ACCOUNT_PASSWORD, password}};
316 : }
317 :
318 : void
319 24 : SipAccountConfig::Credentials::computePasswordHash()
320 : {
321 : pj_md5_context pms;
322 :
323 : /* Compute md5 hash = MD5(username ":" realm ":" password) */
324 24 : pj_md5_init(&pms);
325 24 : pj_md5_update(&pms, (const uint8_t*) username.data(), username.length());
326 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
327 24 : pj_md5_update(&pms, (const uint8_t*) realm.data(), realm.length());
328 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
329 24 : pj_md5_update(&pms, (const uint8_t*) password.data(), password.length());
330 :
331 : unsigned char digest[16];
332 24 : pj_md5_final(&pms, digest);
333 :
334 : char hash[32];
335 :
336 408 : for (int i = 0; i < 16; ++i)
337 384 : pj_val_to_hex_digit(digest[i], &hash[2 * i]);
338 :
339 24 : password_h = {hash, 32};
340 24 : }
341 :
342 : std::vector<std::map<std::string, std::string>>
343 224 : SipAccountConfig::getCredentials() const
344 : {
345 224 : std::vector<std::map<std::string, std::string>> ret;
346 224 : ret.reserve(credentials.size());
347 448 : for (const auto& c : credentials) {
348 224 : ret.emplace_back(c.toMap());
349 : }
350 224 : return ret;
351 0 : }
352 :
353 : void
354 24 : SipAccountConfig::setCredentials(const std::vector<std::map<std::string, std::string>>& creds)
355 : {
356 24 : credentials.clear();
357 24 : credentials.reserve(creds.size());
358 48 : for (const auto& cred : creds)
359 24 : credentials.emplace_back(cred);
360 24 : }
361 :
362 : } // namespace jami
|