File indexing completed on 2024-06-23 04:03:33

0001 /*
0002  * jid.cpp - class for verifying and manipulating Jabber IDs
0003  * Copyright (C) 2003  Justin Karneges
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Lesser General Public
0007  * License as published by the Free Software Foundation; either
0008  * either version 2
0009    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
0010  *
0011  * This library is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014  * Lesser General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU Lesser General Public
0017  * License along with this library; if not, write to the Free Software
0018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0019  *
0020  */
0021 
0022 #undef IRIS_XMPP_JID_DEPRECATED
0023 #include "xmpp/jid/jid.h"
0024 
0025 #include <QCoreApplication>
0026 #include <QByteArray>
0027 #include <QHash>
0028 #include <libidn/stringprep.h>
0029 
0030 using namespace XMPP;
0031 
0032 
0033 //----------------------------------------------------------------------------
0034 // StringPrepCache
0035 //----------------------------------------------------------------------------
0036 class StringPrepCache : public QObject
0037 {
0038 public:
0039     static bool nameprep(const QString &in, int maxbytes, QString& out)
0040     {
0041         if (in.isEmpty()) {
0042             out = QString();
0043             return true;
0044         }
0045 
0046         StringPrepCache *that = get_instance();
0047 
0048         Result *r = that->nameprep_table[in];
0049         if (r) {
0050             if(!r->norm) {
0051                 return false;
0052             }
0053             out = *(r->norm);
0054             return true;
0055         }
0056 
0057         QByteArray cs = in.toUtf8();
0058         cs.resize(maxbytes);
0059         if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
0060         {
0061             that->nameprep_table.insert(in, new Result);
0062             return false;
0063         }
0064 
0065         QString norm = QString::fromUtf8(cs);
0066         that->nameprep_table.insert(in, new Result(norm));
0067         out = norm;
0068         return true;
0069     }
0070 
0071     static bool nodeprep(const QString &in, int maxbytes, QString& out)
0072     {
0073         if(in.isEmpty()) {
0074       out = QString();
0075             return true;
0076         }
0077 
0078         StringPrepCache *that = get_instance();
0079 
0080         Result *r = that->nodeprep_table[in];
0081         if(r) {
0082             if(!r->norm) {
0083                 return false;
0084             }
0085             out = *(r->norm);
0086             return true;
0087         }
0088 
0089         QByteArray cs = in.toUtf8();
0090         cs.resize(maxbytes);
0091         if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0) {
0092             that->nodeprep_table.insert(in, new Result);
0093             return false;
0094         }
0095 
0096         QString norm = QString::fromUtf8(cs);
0097         that->nodeprep_table.insert(in, new Result(norm));
0098         out = norm;
0099         return true;
0100     }
0101 
0102     static bool resourceprep(const QString &in, int maxbytes, QString& out)
0103     {
0104         if(in.isEmpty()) {
0105       out = QString();
0106             return true;
0107         }
0108 
0109         StringPrepCache *that = get_instance();
0110 
0111         Result *r = that->resourceprep_table[in];
0112         if(r) {
0113             if(!r->norm) {
0114                 return false;
0115             }
0116             out = *(r->norm);
0117             return true;
0118         }
0119 
0120         QByteArray cs = in.toUtf8();
0121         cs.resize(maxbytes);
0122         if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0) {
0123             that->resourceprep_table.insert(in, new Result);
0124             return false;
0125         }
0126 
0127         QString norm = QString::fromUtf8(cs);
0128         that->resourceprep_table.insert(in, new Result(norm));
0129         out = norm;
0130         return true;
0131     }
0132 
0133 private:
0134     class Result
0135     {
0136     public:
0137         QString *norm;
0138 
0139         Result() : norm(0)
0140         {
0141         }
0142 
0143         Result(const QString &s) : norm(new QString(s))
0144         {
0145         }
0146 
0147         ~Result()
0148         {
0149             delete norm;
0150         }
0151     };
0152 
0153     QHash<QString,Result*> nameprep_table;
0154     QHash<QString,Result*> nodeprep_table;
0155     QHash<QString,Result*> resourceprep_table;
0156 
0157     static StringPrepCache *instance;
0158 
0159     static StringPrepCache *get_instance()
0160     {
0161         if(!instance)
0162             instance = new StringPrepCache;
0163         return instance;
0164     }
0165 
0166     StringPrepCache()
0167         : QObject(QCoreApplication::instance())
0168     {
0169     }
0170 
0171     ~StringPrepCache() override
0172     {
0173         foreach(Result* r, nameprep_table) {
0174             delete r;
0175         }
0176         nameprep_table.clear();
0177         foreach(Result* r, nodeprep_table) {
0178             delete r;
0179         }
0180         nodeprep_table.clear();
0181         foreach(Result* r, resourceprep_table) {
0182             delete r;
0183         }
0184         resourceprep_table.clear();
0185     }
0186 };
0187 
0188 StringPrepCache *StringPrepCache::instance = 0;
0189 
0190 //----------------------------------------------------------------------------
0191 // Jid
0192 //----------------------------------------------------------------------------
0193 //
0194 static inline bool validDomain(const QString &s, QString& norm)
0195 {
0196     return StringPrepCache::nameprep(s, 1024, norm);
0197 }
0198 
0199 static inline bool validNode(const QString &s, QString& norm)
0200 {
0201     return StringPrepCache::nodeprep(s, 1024, norm);
0202 }
0203 
0204 static inline bool validResource(const QString &s, QString& norm)
0205 {
0206     return StringPrepCache::resourceprep(s, 1024, norm);
0207 }
0208 
0209 Jid::Jid()
0210 {
0211     valid = false;
0212     null = true;
0213 }
0214 
0215 Jid::~Jid()
0216 {
0217 }
0218 
0219 Jid::Jid(const QString &s)
0220 {
0221     set(s);
0222 }
0223 
0224 Jid::Jid(const QString &node, const QString& domain, const QString& resource)
0225 {
0226     set(domain, node, resource);
0227 }
0228 
0229 Jid::Jid(const char *s)
0230 {
0231     set(QString(s));
0232 }
0233 
0234 Jid & Jid::operator=(const QString &s)
0235 {
0236     set(s);
0237     return *this;
0238 }
0239 
0240 Jid & Jid::operator=(const char *s)
0241 {
0242     set(QString(s));
0243     return *this;
0244 }
0245 
0246 void Jid::reset()
0247 {
0248     f = QString();
0249     b = QString();
0250     d = QString();
0251     n = QString();
0252     r = QString();
0253     valid = false;
0254     null = true;
0255 }
0256 
0257 void Jid::update()
0258 {
0259     // build 'bare' and 'full' jids
0260     if(n.isEmpty())
0261         b = d;
0262     else
0263         b = n + '@' + d;
0264     if(r.isEmpty())
0265         f = b;
0266     else
0267         f = b + '/' + r;
0268     if(f.isEmpty())
0269         valid = false;
0270     null = f.isEmpty() && r.isEmpty();
0271 }
0272 
0273 void Jid::set(const QString &s)
0274 {
0275     QString rest, domain, node, resource;
0276     QString norm_domain, norm_node, norm_resource;
0277     int x = s.indexOf('/');
0278     if(x != -1) {
0279         rest = s.mid(0, x);
0280         resource = s.mid(x+1);
0281     }
0282     else {
0283         rest = s;
0284         resource = QString();
0285     }
0286     if(!validResource(resource, norm_resource)) {
0287         reset();
0288         return;
0289     }
0290 
0291     x = rest.indexOf('@');
0292     if(x != -1) {
0293         node = rest.mid(0, x);
0294         domain = rest.mid(x+1);
0295     }
0296     else {
0297         node = QString();
0298         domain = rest;
0299     }
0300     if(!validDomain(domain, norm_domain) || !validNode(node, norm_node)) {
0301         reset();
0302         return;
0303     }
0304 
0305     valid = true;
0306     null = false;
0307     d = norm_domain;
0308     n = norm_node;
0309     r = norm_resource;
0310     update();
0311 }
0312 
0313 void Jid::set(const QString &domain, const QString &node, const QString &resource)
0314 {
0315     QString norm_domain, norm_node, norm_resource;
0316     if(!validDomain(domain, norm_domain) || !validNode(node, norm_node) || !validResource(resource, norm_resource)) {
0317         reset();
0318         return;
0319     }
0320     valid = true;
0321     null = false;
0322     d = norm_domain;
0323     n = norm_node;
0324     r = norm_resource;
0325     update();
0326 }
0327 
0328 void Jid::setDomain(const QString &s)
0329 {
0330     if(!valid)
0331         return;
0332     QString norm;
0333     if(!validDomain(s, norm)) {
0334         reset();
0335         return;
0336     }
0337     d = norm;
0338     update();
0339 }
0340 
0341 void Jid::setNode(const QString &s)
0342 {
0343     if(!valid)
0344         return;
0345     QString norm;
0346     if(!validNode(s, norm)) {
0347         reset();
0348         return;
0349     }
0350     n = norm;
0351     update();
0352 }
0353 
0354 void Jid::setResource(const QString &s)
0355 {
0356     if(!valid)
0357         return;
0358     QString norm;
0359     if(!validResource(s, norm)) {
0360         reset();
0361         return;
0362     }
0363     r = norm;
0364     update();
0365 }
0366 
0367 Jid Jid::withNode(const QString &s) const
0368 {
0369     Jid j = *this;
0370     j.setNode(s);
0371     return j;
0372 }
0373 
0374 Jid Jid::withResource(const QString &s) const
0375 {
0376     Jid j = *this;
0377     j.setResource(s);
0378     return j;
0379 }
0380 
0381 bool Jid::isValid() const
0382 {
0383     return valid;
0384 }
0385 
0386 bool Jid::isEmpty() const
0387 {
0388     return f.isEmpty();
0389 }
0390 
0391 bool Jid::compare(const Jid &a, bool compareRes) const
0392 {
0393     if(null && a.null)
0394         return true;
0395 
0396     // only compare valid jids
0397     if(!valid || !a.valid)
0398         return false;
0399 
0400     if(compareRes ? (f != a.f) : (b != a.b))
0401         return false;
0402 
0403     return true;
0404 }