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 224 : out << YAML::Key << Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE << YAML::Value << registrationExpire;
99 224 : out << YAML::Key << Conf::SERVICE_ROUTE_KEY << YAML::Value << serviceRoute;
100 224 : out << YAML::Key << Conf::ALLOW_IP_AUTO_REWRITE << YAML::Value << allowIPAutoRewrite;
101 224 : out << YAML::Key << Conf::STUN_ENABLED_KEY << YAML::Value << stunEnabled;
102 224 : out << YAML::Key << Conf::STUN_SERVER_KEY << YAML::Value << stunServer;
103 :
104 : // tls submap
105 224 : out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap;
106 224 : out << YAML::Key << Conf::CALIST_KEY << YAML::Value << tlsCaListFile;
107 224 : out << YAML::Key << Conf::CERTIFICATE_KEY << YAML::Value << tlsCertificateFile;
108 224 : out << YAML::Key << Conf::TLS_PASSWORD_KEY << YAML::Value << tlsPassword;
109 224 : out << YAML::Key << Conf::PRIVATE_KEY_KEY << YAML::Value << tlsPrivateKeyFile;
110 224 : out << YAML::Key << Conf::TLS_ENABLE_KEY << YAML::Value << tlsEnable;
111 224 : out << YAML::Key << Conf::TLS_PORT_KEY << YAML::Value << tlsListenerPort;
112 224 : out << YAML::Key << Conf::VERIFY_CLIENT_KEY << YAML::Value << tlsVerifyClient;
113 224 : out << YAML::Key << Conf::VERIFY_SERVER_KEY << YAML::Value << tlsVerifyServer;
114 224 : out << YAML::Key << Conf::REQUIRE_CERTIF_KEY << YAML::Value << tlsRequireClientCertificate;
115 224 : out << YAML::Key << Conf::DISABLE_SECURE_DLG_CHECK << YAML::Value << tlsDisableSecureDlgCheck;
116 224 : out << YAML::Key << Conf::TIMEOUT_KEY << YAML::Value << tlsNegotiationTimeout;
117 224 : out << YAML::Key << Conf::CIPHERS_KEY << YAML::Value << tlsCiphers;
118 224 : out << YAML::Key << Conf::METHOD_KEY << YAML::Value << tlsMethod;
119 224 : out << YAML::Key << Conf::SERVER_KEY << YAML::Value << tlsServerName;
120 224 : out << YAML::EndMap;
121 :
122 : // srtp submap
123 224 : out << YAML::Key << Conf::SRTP_KEY << YAML::Value << YAML::BeginMap;
124 224 : out << YAML::Key << Conf::KEY_EXCHANGE_KEY << YAML::Value << sip_utils::getKeyExchangeName(srtpKeyExchange);
125 224 : out << YAML::Key << Conf::RTP_FALLBACK_KEY << YAML::Value << srtpFallback;
126 224 : out << YAML::EndMap;
127 :
128 224 : out << YAML::EndMap;
129 224 : }
130 :
131 : void
132 0 : SipAccountConfig::unserialize(const YAML::Node& node)
133 : {
134 0 : SipAccountBaseConfig::unserialize(node);
135 0 : parseValueOptional(node, Conf::USERNAME_KEY, username);
136 0 : parseValueOptional(node, Conf::BIND_ADDRESS_KEY, bindAddress);
137 0 : parseValueOptional(node, Conf::PORT_KEY, localPort);
138 0 : parseValueOptional(node, Conf::PUBLISH_PORT_KEY, publishedPort);
139 0 : parseValueOptional(node, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
140 0 : registrationExpire = std::max(MIN_REGISTRATION_TIME, registrationExpire);
141 0 : parseValueOptional(node, Conf::KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
142 0 : parseValueOptional(node, Conf::SERVICE_ROUTE_KEY, serviceRoute);
143 0 : parseValueOptional(node, Conf::ALLOW_IP_AUTO_REWRITE, allowIPAutoRewrite);
144 :
145 0 : parseValueOptional(node, Conf::PRESENCE_MODULE_ENABLED_KEY, presenceEnabled);
146 0 : parseValueOptional(node, Conf::PRESENCE_PUBLISH_SUPPORTED_KEY, publishSupported);
147 0 : parseValueOptional(node, Conf::PRESENCE_SUBSCRIBE_SUPPORTED_KEY, subscribeSupported);
148 :
149 : // ICE - STUN/TURN
150 0 : parseValueOptional(node, Conf::STUN_ENABLED_KEY, stunEnabled);
151 0 : parseValueOptional(node, Conf::STUN_SERVER_KEY, stunServer);
152 :
153 0 : const auto& credsNode = node[Conf::CRED_KEY];
154 0 : setCredentials(
155 0 : parseVectorMap(credsNode,
156 : {Conf::CONFIG_ACCOUNT_REALM, Conf::CONFIG_ACCOUNT_USERNAME, Conf::CONFIG_ACCOUNT_PASSWORD}));
157 :
158 : // get tls submap
159 : try {
160 0 : const auto& tlsMap = node[Conf::TLS_KEY];
161 0 : parseValueOptional(tlsMap, Conf::CERTIFICATE_KEY, tlsCertificateFile);
162 0 : parseValueOptional(tlsMap, Conf::CALIST_KEY, tlsCaListFile);
163 0 : parseValueOptional(tlsMap, Conf::TLS_PASSWORD_KEY, tlsPassword);
164 0 : parseValueOptional(tlsMap, Conf::PRIVATE_KEY_KEY, tlsPrivateKeyFile);
165 0 : parseValueOptional(tlsMap, Conf::TLS_ENABLE_KEY, tlsEnable);
166 0 : parseValueOptional(tlsMap, Conf::TLS_PORT_KEY, tlsListenerPort);
167 0 : parseValueOptional(tlsMap, Conf::CIPHERS_KEY, tlsCiphers);
168 0 : parseValueOptional(tlsMap, Conf::METHOD_KEY, tlsMethod);
169 0 : parseValueOptional(tlsMap, Conf::SERVER_KEY, tlsServerName);
170 0 : parseValueOptional(tlsMap, Conf::REQUIRE_CERTIF_KEY, tlsRequireClientCertificate);
171 0 : parseValueOptional(tlsMap, Conf::VERIFY_CLIENT_KEY, tlsVerifyClient);
172 0 : parseValueOptional(tlsMap, Conf::VERIFY_SERVER_KEY, tlsVerifyServer);
173 0 : parseValueOptional(tlsMap, Conf::DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
174 0 : parseValueOptional(tlsMap, Conf::TIMEOUT_KEY, tlsNegotiationTimeout);
175 0 : } catch (...) {
176 0 : }
177 :
178 : // get srtp submap
179 0 : const auto& srtpMap = node[Conf::SRTP_KEY];
180 0 : std::string tmpKey;
181 0 : parseValueOptional(srtpMap, Conf::KEY_EXCHANGE_KEY, tmpKey);
182 0 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(tmpKey);
183 0 : parseValueOptional(srtpMap, Conf::RTP_FALLBACK_KEY, srtpFallback);
184 0 : }
185 :
186 : std::map<std::string, std::string>
187 91 : SipAccountConfig::toMap() const
188 : {
189 91 : auto a = SipAccountBaseConfig::toMap();
190 : // general sip settings
191 91 : a.emplace(Conf::CONFIG_ACCOUNT_USERNAME, username);
192 91 : a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort));
193 91 : a.emplace(Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
194 91 : a.emplace(Conf::CONFIG_LOCAL_INTERFACE, interface);
195 91 : a.emplace(Conf::CONFIG_PUBLISHED_PORT, std::to_string(publishedPort));
196 91 : a.emplace(Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal ? TRUE_STR : FALSE_STR);
197 91 : a.emplace(Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
198 91 : a.emplace(Conf::CONFIG_STUN_ENABLE, stunEnabled ? TRUE_STR : FALSE_STR);
199 91 : a.emplace(Conf::CONFIG_STUN_SERVER, stunServer);
200 91 : a.emplace(Conf::CONFIG_BIND_ADDRESS, bindAddress);
201 91 : a.emplace(Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
202 91 : a.emplace(Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite ? TRUE_STR : FALSE_STR);
203 91 : a.emplace(Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled ? TRUE_STR : FALSE_STR);
204 91 : a.emplace(Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled ? TRUE_STR : FALSE_STR);
205 91 : a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, std::to_string(registrationExpire));
206 :
207 91 : std::string password {};
208 91 : if (not credentials.empty()) {
209 68 : for (const auto& cred : credentials)
210 68 : if (cred.username == username) {
211 68 : password = cred.password;
212 68 : break;
213 : }
214 : }
215 91 : a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password));
216 :
217 : // srtp settings
218 91 : a.emplace(Conf::CONFIG_SRTP_KEY_EXCHANGE, sip_utils::getKeyExchangeName(srtpKeyExchange));
219 91 : a.emplace(Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback ? TRUE_STR : FALSE_STR);
220 :
221 91 : a.emplace(Conf::CONFIG_TLS_ENABLE, tlsEnable ? TRUE_STR : FALSE_STR);
222 91 : a.emplace(Conf::CONFIG_TLS_LISTENER_PORT, std::to_string(tlsListenerPort));
223 91 : a.emplace(Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile);
224 91 : a.emplace(Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile);
225 91 : a.emplace(Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile);
226 91 : a.emplace(Conf::CONFIG_TLS_PASSWORD, tlsPassword);
227 91 : a.emplace(Conf::CONFIG_TLS_METHOD, tlsMethod);
228 91 : a.emplace(Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
229 91 : a.emplace(Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
230 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer ? TRUE_STR : FALSE_STR);
231 91 : a.emplace(Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient ? TRUE_STR : FALSE_STR);
232 91 : a.emplace(Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate ? TRUE_STR : FALSE_STR);
233 91 : a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, std::to_string(tlsNegotiationTimeout));
234 91 : a.emplace(Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck ? TRUE_STR : FALSE_STR);
235 182 : return a;
236 91 : }
237 :
238 : void
239 24 : SipAccountConfig::fromMap(const std::map<std::string, std::string>& details)
240 : {
241 24 : SipAccountBaseConfig::fromMap(details);
242 :
243 : // general sip settings
244 24 : parseString(details, Conf::CONFIG_ACCOUNT_USERNAME, username);
245 24 : parseInt(details, Conf::CONFIG_LOCAL_PORT, localPort);
246 24 : parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress);
247 24 : parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute);
248 24 : parseBool(details, Conf::CONFIG_ACCOUNT_IP_AUTO_REWRITE, allowIPAutoRewrite);
249 24 : parseString(details, Conf::CONFIG_LOCAL_INTERFACE, interface);
250 24 : parseBool(details, Conf::CONFIG_PUBLISHED_SAMEAS_LOCAL, publishedSameasLocal);
251 24 : parseString(details, Conf::CONFIG_PUBLISHED_ADDRESS, publishedIp);
252 24 : parseInt(details, Conf::CONFIG_PUBLISHED_PORT, publishedPort);
253 24 : parseBool(details, Conf::CONFIG_PRESENCE_ENABLED, presenceEnabled);
254 24 : parseString(details, Conf::CONFIG_ACCOUNT_DTMF_TYPE, dtmfType);
255 24 : parseBool(details, Conf::CONFIG_KEEP_ALIVE_ENABLED, registrationRefreshEnabled);
256 24 : parseInt(details, Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, registrationExpire);
257 :
258 : // srtp settings
259 24 : parseBool(details, Conf::CONFIG_SRTP_RTP_FALLBACK, srtpFallback);
260 24 : auto iter = details.find(Conf::CONFIG_SRTP_KEY_EXCHANGE);
261 24 : if (iter != details.end())
262 24 : srtpKeyExchange = sip_utils::getKeyExchangeProtocol(iter->second);
263 :
264 24 : if (credentials.empty()) { // credentials not set, construct 1 entry
265 24 : JAMI_WARN("No credentials set, inferring them...");
266 24 : std::map<std::string, std::string> map;
267 24 : map[Conf::CONFIG_ACCOUNT_USERNAME] = username;
268 24 : parseString(details, Conf::CONFIG_ACCOUNT_PASSWORD, map[Conf::CONFIG_ACCOUNT_PASSWORD]);
269 24 : map[Conf::CONFIG_ACCOUNT_REALM] = "*";
270 48 : setCredentials({map});
271 24 : }
272 :
273 : // ICE - STUN
274 24 : parseBool(details, Conf::CONFIG_STUN_ENABLE, stunEnabled);
275 24 : parseString(details, Conf::CONFIG_STUN_SERVER, stunServer);
276 :
277 : // TLS
278 24 : parseBool(details, Conf::CONFIG_TLS_ENABLE, tlsEnable);
279 24 : parseInt(details, Conf::CONFIG_TLS_LISTENER_PORT, tlsListenerPort);
280 24 : parsePath(details, Conf::CONFIG_TLS_CA_LIST_FILE, tlsCaListFile, path);
281 24 : parsePath(details, Conf::CONFIG_TLS_CERTIFICATE_FILE, tlsCertificateFile, path);
282 24 : parsePath(details, Conf::CONFIG_TLS_PRIVATE_KEY_FILE, tlsPrivateKeyFile, path);
283 24 : parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword);
284 24 : parseString(details, Conf::CONFIG_TLS_METHOD, tlsMethod);
285 24 : parseString(details, Conf::CONFIG_TLS_CIPHERS, tlsCiphers);
286 24 : parseString(details, Conf::CONFIG_TLS_SERVER_NAME, tlsServerName);
287 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_SERVER, tlsVerifyServer);
288 24 : parseBool(details, Conf::CONFIG_TLS_VERIFY_CLIENT, tlsVerifyClient);
289 24 : parseBool(details, Conf::CONFIG_TLS_REQUIRE_CLIENT_CERTIFICATE, tlsRequireClientCertificate);
290 24 : parseBool(details, Conf::CONFIG_TLS_DISABLE_SECURE_DLG_CHECK, tlsDisableSecureDlgCheck);
291 24 : parseInt(details, Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC, tlsNegotiationTimeout);
292 24 : }
293 :
294 24 : SipAccountConfig::Credentials::Credentials(const std::map<std::string, std::string>& cred)
295 : {
296 24 : auto itrealm = cred.find(Conf::CONFIG_ACCOUNT_REALM);
297 24 : auto user = cred.find(Conf::CONFIG_ACCOUNT_USERNAME);
298 24 : auto passw = cred.find(Conf::CONFIG_ACCOUNT_PASSWORD);
299 24 : realm = itrealm != cred.end() ? itrealm->second : "";
300 24 : username = user != cred.end() ? user->second : "";
301 24 : password = passw != cred.end() ? passw->second : "";
302 24 : computePasswordHash();
303 24 : }
304 :
305 : std::map<std::string, std::string>
306 224 : SipAccountConfig::Credentials::toMap() const
307 : {
308 224 : return {{Conf::CONFIG_ACCOUNT_REALM, realm},
309 224 : {Conf::CONFIG_ACCOUNT_USERNAME, username},
310 896 : {Conf::CONFIG_ACCOUNT_PASSWORD, password}};
311 : }
312 :
313 : void
314 24 : SipAccountConfig::Credentials::computePasswordHash()
315 : {
316 : pj_md5_context pms;
317 :
318 : /* Compute md5 hash = MD5(username ":" realm ":" password) */
319 24 : pj_md5_init(&pms);
320 24 : pj_md5_update(&pms, (const uint8_t*) username.data(), username.length());
321 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
322 24 : pj_md5_update(&pms, (const uint8_t*) realm.data(), realm.length());
323 24 : pj_md5_update(&pms, (const uint8_t*) ":", 1);
324 24 : pj_md5_update(&pms, (const uint8_t*) password.data(), password.length());
325 :
326 : unsigned char digest[16];
327 24 : pj_md5_final(&pms, digest);
328 :
329 : char hash[32];
330 :
331 408 : for (int i = 0; i < 16; ++i)
332 384 : pj_val_to_hex_digit(digest[i], &hash[2 * i]);
333 :
334 24 : password_h = {hash, 32};
335 24 : }
336 :
337 : std::vector<std::map<std::string, std::string>>
338 224 : SipAccountConfig::getCredentials() const
339 : {
340 224 : std::vector<std::map<std::string, std::string>> ret;
341 224 : ret.reserve(credentials.size());
342 448 : for (const auto& c : credentials) {
343 224 : ret.emplace_back(c.toMap());
344 : }
345 224 : return ret;
346 0 : }
347 :
348 : void
349 24 : SipAccountConfig::setCredentials(const std::vector<std::map<std::string, std::string>>& creds)
350 : {
351 24 : credentials.clear();
352 24 : credentials.reserve(creds.size());
353 48 : for (const auto& cred : creds)
354 24 : credentials.emplace_back(cred);
355 24 : }
356 :
357 : } // namespace jami
|