QXmppSasl.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. /*
  2. * Copyright (C) 2008-2012 The QXmpp developers
  3. *
  4. * Authors:
  5. * Manjeet Dahiya
  6. * Jeremy Lainé
  7. *
  8. * Source:
  9. * http://code.google.com/p/qxmpp
  10. *
  11. * This file is a part of QXmpp library.
  12. *
  13. * This library is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU Lesser General Public
  15. * License as published by the Free Software Foundation; either
  16. * version 2.1 of the License, or (at your option) any later version.
  17. *
  18. * This library is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21. * Lesser General Public License for more details.
  22. *
  23. */
  24. #include <cstdlib>
  25. #include <QCryptographicHash>
  26. #include <QDomElement>
  27. #include <QStringList>
  28. #include <QUrl>
  29. #include "QXmppSasl_p.h"
  30. #include "QXmppUtils.h"
  31. const char *ns_xmpp_sasl = "urn:ietf:params:xml:ns:xmpp-sasl";
  32. static QByteArray forcedNonce;
  33. // Calculate digest response for use with XMPP/SASL.
  34. static QByteArray calculateDigest(const QByteArray &method, const QByteArray &digestUri, const QByteArray &secret, const QByteArray &nonce, const QByteArray &cnonce, const QByteArray &nc)
  35. {
  36. const QByteArray A1 = secret + ':' + nonce + ':' + cnonce;
  37. const QByteArray A2 = method + ':' + digestUri;
  38. QByteArray HA1 = QCryptographicHash::hash(A1, QCryptographicHash::Md5).toHex();
  39. QByteArray HA2 = QCryptographicHash::hash(A2, QCryptographicHash::Md5).toHex();
  40. const QByteArray KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ":auth:" + HA2;
  41. return QCryptographicHash::hash(KD, QCryptographicHash::Md5).toHex();
  42. }
  43. static QByteArray generateNonce()
  44. {
  45. if (!forcedNonce.isEmpty())
  46. return forcedNonce;
  47. QByteArray nonce = QXmppUtils::generateRandomBytes(32);
  48. // The random data can the '=' char is not valid as it is a delimiter,
  49. // so to be safe, base64 the nonce
  50. return nonce.toBase64();
  51. }
  52. QXmppSaslAuth::QXmppSaslAuth(const QString &mechanism, const QByteArray &value)
  53. : m_mechanism(mechanism)
  54. , m_value(value)
  55. {
  56. }
  57. QString QXmppSaslAuth::mechanism() const
  58. {
  59. return m_mechanism;
  60. }
  61. void QXmppSaslAuth::setMechanism(const QString &mechanism)
  62. {
  63. m_mechanism = mechanism;
  64. }
  65. QByteArray QXmppSaslAuth::value() const
  66. {
  67. return m_value;
  68. }
  69. void QXmppSaslAuth::setValue(const QByteArray &value)
  70. {
  71. m_value = value;
  72. }
  73. void QXmppSaslAuth::parse(const QDomElement &element)
  74. {
  75. m_mechanism = element.attribute("mechanism");
  76. m_value = QByteArray::fromBase64(element.text().toAscii());
  77. }
  78. void QXmppSaslAuth::toXml(QXmlStreamWriter *writer) const
  79. {
  80. writer->writeStartElement("auth");
  81. writer->writeAttribute("xmlns", ns_xmpp_sasl);
  82. writer->writeAttribute("mechanism", m_mechanism);
  83. if (!m_value.isEmpty())
  84. writer->writeCharacters(m_value.toBase64());
  85. writer->writeEndElement();
  86. }
  87. QXmppSaslChallenge::QXmppSaslChallenge(const QByteArray &value)
  88. : m_value(value)
  89. {
  90. }
  91. QByteArray QXmppSaslChallenge::value() const
  92. {
  93. return m_value;
  94. }
  95. void QXmppSaslChallenge::setValue(const QByteArray &value)
  96. {
  97. m_value = value;
  98. }
  99. void QXmppSaslChallenge::parse(const QDomElement &element)
  100. {
  101. m_value = QByteArray::fromBase64(element.text().toAscii());
  102. }
  103. void QXmppSaslChallenge::toXml(QXmlStreamWriter *writer) const
  104. {
  105. writer->writeStartElement("challenge");
  106. writer->writeAttribute("xmlns", ns_xmpp_sasl);
  107. if (!m_value.isEmpty())
  108. writer->writeCharacters(m_value.toBase64());
  109. writer->writeEndElement();
  110. }
  111. QXmppSaslFailure::QXmppSaslFailure(const QString &condition)
  112. : m_condition(condition)
  113. {
  114. }
  115. QString QXmppSaslFailure::condition() const
  116. {
  117. return m_condition;
  118. }
  119. void QXmppSaslFailure::setCondition(const QString &condition)
  120. {
  121. m_condition = condition;
  122. }
  123. void QXmppSaslFailure::parse(const QDomElement &element)
  124. {
  125. m_condition = element.firstChildElement().tagName();
  126. }
  127. void QXmppSaslFailure::toXml(QXmlStreamWriter *writer) const
  128. {
  129. writer->writeStartElement("failure");
  130. writer->writeAttribute("xmlns", ns_xmpp_sasl);
  131. if (!m_condition.isEmpty())
  132. writer->writeEmptyElement(m_condition);
  133. writer->writeEndElement();
  134. }
  135. QXmppSaslResponse::QXmppSaslResponse(const QByteArray &value)
  136. : m_value(value)
  137. {
  138. }
  139. QByteArray QXmppSaslResponse::value() const
  140. {
  141. return m_value;
  142. }
  143. void QXmppSaslResponse::setValue(const QByteArray &value)
  144. {
  145. m_value = value;
  146. }
  147. void QXmppSaslResponse::parse(const QDomElement &element)
  148. {
  149. m_value = QByteArray::fromBase64(element.text().toAscii());
  150. }
  151. void QXmppSaslResponse::toXml(QXmlStreamWriter *writer) const
  152. {
  153. writer->writeStartElement("response");
  154. writer->writeAttribute("xmlns", ns_xmpp_sasl);
  155. if (!m_value.isEmpty())
  156. writer->writeCharacters(m_value.toBase64());
  157. writer->writeEndElement();
  158. }
  159. QXmppSaslSuccess::QXmppSaslSuccess()
  160. {
  161. }
  162. void QXmppSaslSuccess::parse(const QDomElement &element)
  163. {
  164. Q_UNUSED(element);
  165. }
  166. void QXmppSaslSuccess::toXml(QXmlStreamWriter *writer) const
  167. {
  168. writer->writeStartElement("success");
  169. writer->writeAttribute("xmlns", ns_xmpp_sasl);
  170. writer->writeEndElement();
  171. }
  172. class QXmppSaslClientPrivate
  173. {
  174. public:
  175. QString host;
  176. QString serviceType;
  177. QString username;
  178. QString password;
  179. };
  180. QXmppSaslClient::QXmppSaslClient(QObject *parent)
  181. : QXmppLoggable(parent)
  182. , d(new QXmppSaslClientPrivate)
  183. {
  184. }
  185. QXmppSaslClient::~QXmppSaslClient()
  186. {
  187. delete d;
  188. }
  189. /// Returns a list of supported mechanisms.
  190. QStringList QXmppSaslClient::availableMechanisms()
  191. {
  192. return QStringList() << "PLAIN" << "DIGEST-MD5" << "ANONYMOUS" << "X-FACEBOOK-PLATFORM" << "X-MESSENGER-OAUTH2" << "X-OAUTH2";
  193. }
  194. /// Creates an SASL client for the given mechanism.
  195. QXmppSaslClient* QXmppSaslClient::create(const QString &mechanism, QObject *parent)
  196. {
  197. if (mechanism == "PLAIN") {
  198. return new QXmppSaslClientPlain(parent);
  199. } else if (mechanism == "DIGEST-MD5") {
  200. return new QXmppSaslClientDigestMd5(parent);
  201. } else if (mechanism == "ANONYMOUS") {
  202. return new QXmppSaslClientAnonymous(parent);
  203. } else if (mechanism == "X-FACEBOOK-PLATFORM") {
  204. return new QXmppSaslClientFacebook(parent);
  205. } else if (mechanism == "X-MESSENGER-OAUTH2") {
  206. return new QXmppSaslClientWindowsLive(parent);
  207. } else if (mechanism == "X-OAUTH2") {
  208. return new QXmppSaslClientGoogle(parent);
  209. } else {
  210. return 0;
  211. }
  212. }
  213. /// Returns the host.
  214. QString QXmppSaslClient::host() const
  215. {
  216. return d->host;
  217. }
  218. /// Sets the host.
  219. void QXmppSaslClient::setHost(const QString &host)
  220. {
  221. d->host = host;
  222. }
  223. /// Returns the service type, e.g. "xmpp".
  224. QString QXmppSaslClient::serviceType() const
  225. {
  226. return d->serviceType;
  227. }
  228. /// Sets the service type, e.g. "xmpp".
  229. void QXmppSaslClient::setServiceType(const QString &serviceType)
  230. {
  231. d->serviceType = serviceType;
  232. }
  233. /// Returns the username.
  234. QString QXmppSaslClient::username() const
  235. {
  236. return d->username;
  237. }
  238. /// Sets the username.
  239. void QXmppSaslClient::setUsername(const QString &username)
  240. {
  241. d->username = username;
  242. }
  243. /// Returns the password.
  244. QString QXmppSaslClient::password() const
  245. {
  246. return d->password;
  247. }
  248. /// Sets the password.
  249. void QXmppSaslClient::setPassword(const QString &password)
  250. {
  251. d->password = password;
  252. }
  253. QXmppSaslClientAnonymous::QXmppSaslClientAnonymous(QObject *parent)
  254. : QXmppSaslClient(parent)
  255. , m_step(0)
  256. {
  257. }
  258. QString QXmppSaslClientAnonymous::mechanism() const
  259. {
  260. return "ANONYMOUS";
  261. }
  262. bool QXmppSaslClientAnonymous::respond(const QByteArray &challenge, QByteArray &response)
  263. {
  264. Q_UNUSED(challenge);
  265. if (m_step == 0) {
  266. response = QByteArray();
  267. m_step++;
  268. return true;
  269. } else {
  270. warning("QXmppSaslClientAnonymous : Invalid step");
  271. return false;
  272. }
  273. }
  274. QXmppSaslClientDigestMd5::QXmppSaslClientDigestMd5(QObject *parent)
  275. : QXmppSaslClient(parent)
  276. , m_nc("00000001")
  277. , m_step(0)
  278. {
  279. m_cnonce = generateNonce();
  280. }
  281. QString QXmppSaslClientDigestMd5::mechanism() const
  282. {
  283. return "DIGEST-MD5";
  284. }
  285. bool QXmppSaslClientDigestMd5::respond(const QByteArray &challenge, QByteArray &response)
  286. {
  287. Q_UNUSED(challenge);
  288. const QByteArray digestUri = QString("%1/%2").arg(serviceType(), host()).toUtf8();
  289. if (m_step == 0) {
  290. response = QByteArray();
  291. m_step++;
  292. return true;
  293. } else if (m_step == 1) {
  294. const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(challenge);
  295. if (!input.contains("nonce")) {
  296. warning("QXmppSaslClientDigestMd5 : Invalid input on step 1");
  297. return false;
  298. }
  299. // determine realm
  300. const QByteArray realm = input.value("realm");
  301. // determine quality of protection
  302. const QList<QByteArray> qops = input.value("qop", "auth").split(',');
  303. if (!qops.contains("auth")) {
  304. warning("QXmppSaslClientDigestMd5 : Invalid quality of protection");
  305. return false;
  306. }
  307. m_nonce = input.value("nonce");
  308. m_secret = QCryptographicHash::hash(
  309. username().toUtf8() + ":" + realm + ":" + password().toUtf8(),
  310. QCryptographicHash::Md5);
  311. // Build response
  312. QMap<QByteArray, QByteArray> output;
  313. output["username"] = username().toUtf8();
  314. if (!realm.isEmpty())
  315. output["realm"] = realm;
  316. output["nonce"] = m_nonce;
  317. output["qop"] = "auth";
  318. output["cnonce"] = m_cnonce;
  319. output["nc"] = m_nc;
  320. output["digest-uri"] = digestUri;
  321. output["response"] = calculateDigest("AUTHENTICATE", digestUri, m_secret, m_nonce, m_cnonce, m_nc);
  322. output["charset"] = "utf-8";
  323. response = QXmppSaslDigestMd5::serializeMessage(output);
  324. m_step++;
  325. return true;
  326. } else if (m_step == 2) {
  327. const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(challenge);
  328. // check new challenge
  329. if (input.value("rspauth") != calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc)) {
  330. warning("QXmppSaslClientDigestMd5 : Invalid challenge on step 2");
  331. return false;
  332. }
  333. response = QByteArray();
  334. m_step++;
  335. return true;
  336. } else {
  337. warning("QXmppSaslClientDigestMd5 : Invalid step");
  338. return false;
  339. }
  340. }
  341. QXmppSaslClientFacebook::QXmppSaslClientFacebook(QObject *parent)
  342. : QXmppSaslClient(parent)
  343. , m_step(0)
  344. {
  345. }
  346. QString QXmppSaslClientFacebook::mechanism() const
  347. {
  348. return "X-FACEBOOK-PLATFORM";
  349. }
  350. bool QXmppSaslClientFacebook::respond(const QByteArray &challenge, QByteArray &response)
  351. {
  352. if (m_step == 0) {
  353. // no initial response
  354. response = QByteArray();
  355. m_step++;
  356. return true;
  357. } else if (m_step == 1) {
  358. // parse request
  359. QUrl requestUrl;
  360. requestUrl.setEncodedQuery(challenge);
  361. if (!requestUrl.hasQueryItem("method") || !requestUrl.hasQueryItem("nonce")) {
  362. warning("QXmppSaslClientFacebook : Invalid challenge, nonce or method missing");
  363. return false;
  364. }
  365. // build response
  366. QUrl responseUrl;
  367. responseUrl.addQueryItem("access_token", password());
  368. responseUrl.addQueryItem("api_key", username());
  369. responseUrl.addQueryItem("call_id", 0);
  370. responseUrl.addQueryItem("method", requestUrl.queryItemValue("method"));
  371. responseUrl.addQueryItem("nonce", requestUrl.queryItemValue("nonce"));
  372. responseUrl.addQueryItem("v", "1.0");
  373. response = responseUrl.encodedQuery();
  374. m_step++;
  375. return true;
  376. } else {
  377. warning("QXmppSaslClientFacebook : Invalid step");
  378. return false;
  379. }
  380. }
  381. QXmppSaslClientGoogle::QXmppSaslClientGoogle(QObject *parent)
  382. : QXmppSaslClient(parent)
  383. , m_step(0)
  384. {
  385. }
  386. QString QXmppSaslClientGoogle::mechanism() const
  387. {
  388. return "X-OAUTH2";
  389. }
  390. bool QXmppSaslClientGoogle::respond(const QByteArray &challenge, QByteArray &response)
  391. {
  392. Q_UNUSED(challenge);
  393. if (m_step == 0) {
  394. // send initial response
  395. response = QString('\0' + username() + '\0' + password()).toUtf8();
  396. m_step++;
  397. return true;
  398. } else {
  399. warning("QXmppSaslClientGoogle : Invalid step");
  400. return false;
  401. }
  402. }
  403. QXmppSaslClientPlain::QXmppSaslClientPlain(QObject *parent)
  404. : QXmppSaslClient(parent)
  405. , m_step(0)
  406. {
  407. }
  408. QString QXmppSaslClientPlain::mechanism() const
  409. {
  410. return "PLAIN";
  411. }
  412. bool QXmppSaslClientPlain::respond(const QByteArray &challenge, QByteArray &response)
  413. {
  414. Q_UNUSED(challenge);
  415. if (m_step == 0) {
  416. response = QString('\0' + username() + '\0' + password()).toUtf8();
  417. m_step++;
  418. return true;
  419. } else {
  420. warning("QXmppSaslClientPlain : Invalid step");
  421. return false;
  422. }
  423. }
  424. QXmppSaslClientWindowsLive::QXmppSaslClientWindowsLive(QObject *parent)
  425. : QXmppSaslClient(parent)
  426. , m_step(0)
  427. {
  428. }
  429. QString QXmppSaslClientWindowsLive::mechanism() const
  430. {
  431. return "X-MESSENGER-OAUTH2";
  432. }
  433. bool QXmppSaslClientWindowsLive::respond(const QByteArray &challenge, QByteArray &response)
  434. {
  435. Q_UNUSED(challenge);
  436. if (m_step == 0) {
  437. // send initial response
  438. response = QByteArray::fromBase64(password().toLatin1());
  439. m_step++;
  440. return true;
  441. } else {
  442. warning("QXmppSaslClientWindowsLive : Invalid step");
  443. return false;
  444. }
  445. }
  446. class QXmppSaslServerPrivate
  447. {
  448. public:
  449. QString username;
  450. QString password;
  451. QByteArray passwordDigest;
  452. QString realm;
  453. };
  454. QXmppSaslServer::QXmppSaslServer(QObject *parent)
  455. : QXmppLoggable(parent)
  456. , d(new QXmppSaslServerPrivate)
  457. {
  458. }
  459. QXmppSaslServer::~QXmppSaslServer()
  460. {
  461. delete d;
  462. }
  463. /// Creates an SASL server for the given mechanism.
  464. QXmppSaslServer* QXmppSaslServer::create(const QString &mechanism, QObject *parent)
  465. {
  466. if (mechanism == "PLAIN") {
  467. return new QXmppSaslServerPlain(parent);
  468. } else if (mechanism == "DIGEST-MD5") {
  469. return new QXmppSaslServerDigestMd5(parent);
  470. } else if (mechanism == "ANONYMOUS") {
  471. return new QXmppSaslServerAnonymous(parent);
  472. } else {
  473. return 0;
  474. }
  475. }
  476. /// Returns the username.
  477. QString QXmppSaslServer::username() const
  478. {
  479. return d->username;
  480. }
  481. /// Sets the username.
  482. void QXmppSaslServer::setUsername(const QString &username)
  483. {
  484. d->username = username;
  485. }
  486. /// Returns the password.
  487. QString QXmppSaslServer::password() const
  488. {
  489. return d->password;
  490. }
  491. /// Sets the password.
  492. void QXmppSaslServer::setPassword(const QString &password)
  493. {
  494. d->password = password;
  495. }
  496. /// Returns the password digest.
  497. QByteArray QXmppSaslServer::passwordDigest() const
  498. {
  499. return d->passwordDigest;
  500. }
  501. /// Sets the password digest.
  502. void QXmppSaslServer::setPasswordDigest(const QByteArray &digest)
  503. {
  504. d->passwordDigest = digest;
  505. }
  506. /// Returns the realm.
  507. QString QXmppSaslServer::realm() const
  508. {
  509. return d->realm;
  510. }
  511. /// Sets the realm.
  512. void QXmppSaslServer::setRealm(const QString &realm)
  513. {
  514. d->realm = realm;
  515. }
  516. QXmppSaslServerAnonymous::QXmppSaslServerAnonymous(QObject *parent)
  517. : QXmppSaslServer(parent)
  518. , m_step(0)
  519. {
  520. }
  521. QString QXmppSaslServerAnonymous::mechanism() const
  522. {
  523. return "ANONYMOUS";
  524. }
  525. QXmppSaslServer::Response QXmppSaslServerAnonymous::respond(const QByteArray &request, QByteArray &response)
  526. {
  527. Q_UNUSED(request);
  528. if (m_step == 0) {
  529. m_step++;
  530. response = QByteArray();
  531. return Succeeded;
  532. } else {
  533. warning("QXmppSaslServerAnonymous : Invalid step");
  534. return Failed;
  535. }
  536. }
  537. QXmppSaslServerDigestMd5::QXmppSaslServerDigestMd5(QObject *parent)
  538. : QXmppSaslServer(parent)
  539. , m_step(0)
  540. {
  541. m_nonce = generateNonce();
  542. }
  543. QString QXmppSaslServerDigestMd5::mechanism() const
  544. {
  545. return "DIGEST-MD5";
  546. }
  547. QXmppSaslServer::Response QXmppSaslServerDigestMd5::respond(const QByteArray &request, QByteArray &response)
  548. {
  549. if (m_step == 0) {
  550. QMap<QByteArray, QByteArray> output;
  551. output["nonce"] = m_nonce;
  552. if (!realm().isEmpty())
  553. output["realm"] = realm().toUtf8();
  554. output["qop"] = "auth";
  555. output["charset"] = "utf-8";
  556. output["algorithm"] = "md5-sess";
  557. m_step++;
  558. response = QXmppSaslDigestMd5::serializeMessage(output);
  559. return Challenge;
  560. } else if (m_step == 1) {
  561. const QMap<QByteArray, QByteArray> input = QXmppSaslDigestMd5::parseMessage(request);
  562. const QByteArray realm = input.value("realm");
  563. const QByteArray digestUri = input.value("digest-uri");
  564. if (input.value("qop") != "auth") {
  565. warning("QXmppSaslServerDigestMd5 : Invalid quality of protection");
  566. return Failed;
  567. }
  568. setUsername(QString::fromUtf8(input.value("username")));
  569. if (password().isEmpty() && passwordDigest().isEmpty())
  570. return InputNeeded;
  571. m_nc = input.value("nc");
  572. m_cnonce = input.value("cnonce");
  573. if (!password().isEmpty()) {
  574. m_secret = QCryptographicHash::hash(
  575. username().toUtf8() + ":" + realm + ":" + password().toUtf8(),
  576. QCryptographicHash::Md5);
  577. } else {
  578. m_secret = passwordDigest();
  579. }
  580. if (input.value("response") != calculateDigest("AUTHENTICATE", digestUri, m_secret, m_nonce, m_cnonce, m_nc))
  581. return Failed;
  582. QMap<QByteArray, QByteArray> output;
  583. output["rspauth"] = calculateDigest(QByteArray(), digestUri, m_secret, m_nonce, m_cnonce, m_nc);
  584. m_step++;
  585. response = QXmppSaslDigestMd5::serializeMessage(output);
  586. return Challenge;
  587. } else if (m_step == 2) {
  588. m_step++;
  589. response = QByteArray();
  590. return Succeeded;
  591. } else {
  592. warning("QXmppSaslServerDigestMd5 : Invalid step");
  593. return Failed;
  594. }
  595. }
  596. QXmppSaslServerPlain::QXmppSaslServerPlain(QObject *parent)
  597. : QXmppSaslServer(parent)
  598. , m_step(0)
  599. {
  600. }
  601. QString QXmppSaslServerPlain::mechanism() const
  602. {
  603. return "PLAIN";
  604. }
  605. QXmppSaslServer::Response QXmppSaslServerPlain::respond(const QByteArray &request, QByteArray &response)
  606. {
  607. if (m_step == 0) {
  608. if (request.isEmpty()) {
  609. response = QByteArray();
  610. return Challenge;
  611. }
  612. QList<QByteArray> auth = request.split('\0');
  613. if (auth.size() != 3) {
  614. warning("QXmppSaslServerPlain : Invalid input");
  615. return Failed;
  616. }
  617. setUsername(QString::fromUtf8(auth[1]));
  618. setPassword(QString::fromUtf8(auth[2]));
  619. m_step++;
  620. response = QByteArray();
  621. return InputNeeded;
  622. } else {
  623. warning("QXmppSaslServerPlain : Invalid step");
  624. return Failed;
  625. }
  626. }
  627. void QXmppSaslDigestMd5::setNonce(const QByteArray &nonce)
  628. {
  629. forcedNonce = nonce;
  630. }
  631. QMap<QByteArray, QByteArray> QXmppSaslDigestMd5::parseMessage(const QByteArray &ba)
  632. {
  633. QMap<QByteArray, QByteArray> map;
  634. int startIndex = 0;
  635. int pos = 0;
  636. while ((pos = ba.indexOf("=", startIndex)) >= 0)
  637. {
  638. // key get name and skip equals
  639. const QByteArray key = ba.mid(startIndex, pos - startIndex).trimmed();
  640. pos++;
  641. // check whether string is quoted
  642. if (ba.at(pos) == '"')
  643. {
  644. // skip opening quote
  645. pos++;
  646. int endPos = ba.indexOf('"', pos);
  647. // skip quoted quotes
  648. while (endPos >= 0 && ba.at(endPos - 1) == '\\')
  649. endPos = ba.indexOf('"', endPos + 1);
  650. if (endPos < 0)
  651. {
  652. qWarning("Unfinished quoted string");
  653. return map;
  654. }
  655. // unquote
  656. QByteArray value = ba.mid(pos, endPos - pos);
  657. value.replace("\\\"", "\"");
  658. value.replace("\\\\", "\\");
  659. map[key] = value;
  660. // skip closing quote and comma
  661. startIndex = endPos + 2;
  662. } else {
  663. // non-quoted string
  664. int endPos = ba.indexOf(',', pos);
  665. if (endPos < 0)
  666. endPos = ba.size();
  667. map[key] = ba.mid(pos, endPos - pos);
  668. // skip comma
  669. startIndex = endPos + 1;
  670. }
  671. }
  672. return map;
  673. }
  674. QByteArray QXmppSaslDigestMd5::serializeMessage(const QMap<QByteArray, QByteArray> &map)
  675. {
  676. QByteArray ba;
  677. foreach (const QByteArray &key, map.keys())
  678. {
  679. if (!ba.isEmpty())
  680. ba.append(',');
  681. ba.append(key + "=");
  682. QByteArray value = map[key];
  683. const char *separators = "()<>@,;:\\\"/[]?={} \t";
  684. bool quote = false;
  685. for (const char *c = separators; *c; c++)
  686. {
  687. if (value.contains(*c))
  688. {
  689. quote = true;
  690. break;
  691. }
  692. }
  693. if (quote)
  694. {
  695. value.replace("\\", "\\\\");
  696. value.replace("\"", "\\\"");
  697. ba.append("\"" + value + "\"");
  698. }
  699. else
  700. ba.append(value);
  701. }
  702. return ba;
  703. }