KCalendarCore

person.cpp
Go to the documentation of this file.
1 /*
2  This file is part of the kcalcore library.
3 
4  SPDX-FileCopyrightText: 2001 Cornelius Schumacher <[email protected]>
5  SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <[email protected]>
6  SPDX-FileCopyrightText: 2010 Casey Link <[email protected]>
7  SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <[email protected]>
8 
9  SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 /**
12  @file
13  This file is part of the API for handling calendar data and
14  defines the Person class.
15 
16  @brief
17  Represents a person, by name and email address.
18 
19  @author Cornelius Schumacher <[email protected]>
20  @author Reinhold Kainhofer <[email protected]>
21 */
22 
23 #include "person.h"
24 #include "person_p.h"
25 
26 #include <QDataStream>
27 #include <QRegularExpression>
28 
29 using namespace KCalendarCore;
30 
31 /**
32  Private class that helps to provide binary compatibility between releases.
33  @internal
34 */
35 //@cond PRIVATE
36 class Q_DECL_HIDDEN KCalendarCore::Person::Private : public QSharedData
37 {
38 public:
39  QString mName; // person name
40  QString mEmail; // person email address
41 };
42 //@endcond
43 
45  : d(new KCalendarCore::Person::Private)
46 {
47 }
48 
49 Person::Person(const QString &name, const QString &email)
50  : d(new KCalendarCore::Person::Private)
51 {
52  d->mName = name;
53  d->mEmail = email;
54 }
55 
56 Person::Person(const Person &person)
57  : d(person.d)
58 {
59 }
60 
61 Person::~Person() = default;
62 
63 bool KCalendarCore::Person::operator==(const Person &person) const
64 {
65  return d->mName == person.d->mName && d->mEmail == person.d->mEmail;
66 }
67 
68 bool KCalendarCore::Person::operator!=(const Person &person) const
69 {
70  return !(*this == person);
71 }
72 
74 {
75  // check for self assignment
76  if (&person == this) {
77  return *this;
78  }
79 
80  d = person.d;
81  return *this;
82 }
83 
84 QString KCalendarCore::fullNameHelper(const QString &name, const QString &email)
85 {
86  if (name.isEmpty()) {
87  return email;
88  }
89  if (email.isEmpty()) {
90  return name;
91  }
92  // Taken from KContacts::Addressee::fullEmail
94  const QRegularExpression needQuotes(QStringLiteral("[^ 0-9A-Za-z\\x{0080}-\\x{FFFF}]"));
95  bool weNeedToQuote = name.indexOf(needQuotes) != -1;
96  if (weNeedToQuote) {
97  if (fullName[0] != QLatin1Char('"')) {
99  }
100  if (fullName[fullName.length() - 1] != QLatin1Char('"')) {
102  }
103  }
104  return fullName + QStringLiteral(" <") + email + QLatin1Char('>');
105 }
106 
107 QString Person::fullName() const
108 {
109  return fullNameHelper(d->mName, d->mEmail);
110 }
111 
112 QString Person::name() const
113 {
114  return d->mName;
115 }
116 
117 QString Person::email() const
118 {
119  return d->mEmail;
120 }
121 
122 bool Person::isEmpty() const
123 {
124  return d->mEmail.isEmpty() && d->mName.isEmpty();
125 }
126 
127 void Person::setName(const QString &name)
128 {
129  d->mName = name;
130 }
131 
132 void Person::setEmail(const QString &email)
133 {
134  if (email.startsWith(QLatin1String("mailto:"), Qt::CaseInsensitive)) {
135  d->mEmail = email.mid(7);
136  } else {
137  d->mEmail = email;
138  }
139 }
140 
141 bool Person::isValidEmail(const QString &email)
142 {
143  const int pos = email.lastIndexOf(QLatin1Char('@'));
144  return (pos > 0) && (email.lastIndexOf(QLatin1Char('.')) > pos) && ((email.length() - pos) > 4);
145 }
146 
148 {
149  return qHash(key.fullName());
150 }
151 
153 {
154  return stream << person.d->mName << person.d->mEmail << (int)(0);
155 }
156 
158 {
159  int count;
160  stream >> person.d->mName >> person.d->mEmail >> count;
161  return stream;
162 }
163 
164 // The following function was lifted directly from KPIMUtils
165 // in order to eliminate the dependency on that library.
166 // Any changes made here should be ported there, and vice versa.
167 static bool extractEmailAddressAndName(const QString &aStr, QString &mail, QString &name)
168 {
169  name.clear();
170  mail.clear();
171 
172  const int len = aStr.length();
173  const char cQuotes = '"';
174 
175  bool bInComment = false;
176  bool bInQuotesOutsideOfEmail = false;
177  int i = 0;
178  int iAd = 0;
179  int iMailStart = 0;
180  int iMailEnd = 0;
181  QChar c;
182  unsigned int commentstack = 0;
183 
184  // Find the '@' of the email address
185  // skipping all '@' inside "(...)" comments:
186  while (i < len) {
187  c = aStr[i];
188  if (QLatin1Char('(') == c) {
189  commentstack++;
190  }
191  if (QLatin1Char(')') == c) {
192  commentstack--;
193  }
194  bInComment = commentstack != 0;
195  if (QLatin1Char('"') == c && !bInComment) {
196  bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail;
197  }
198 
199  if (!bInComment && !bInQuotesOutsideOfEmail) {
200  if (QLatin1Char('@') == c) {
201  iAd = i;
202  break; // found it
203  }
204  }
205  ++i;
206  }
207 
208  if (!iAd) {
209  // We suppose the user is typing the string manually and just
210  // has not finished typing the mail address part.
211  // So we take everything that's left of the '<' as name and the rest as mail
212  for (i = 0; len > i; ++i) {
213  c = aStr[i];
214  if (QLatin1Char('<') != c) {
215  name.append(c);
216  } else {
217  break;
218  }
219  }
220  mail = aStr.mid(i + 1);
221  if (mail.endsWith(QLatin1Char('>'))) {
222  mail.chop(1);
223  }
224 
225  } else {
226  // Loop backwards until we find the start of the string
227  // or a ',' that is outside of a comment
228  // and outside of quoted text before the leading '<'.
229  bInComment = false;
230  bInQuotesOutsideOfEmail = false;
231  for (i = iAd - 1; 0 <= i; --i) {
232  c = aStr[i];
233  if (bInComment) {
234  if (QLatin1Char('(') == c) {
235  if (!name.isEmpty()) {
236  name.prepend(QLatin1Char(' '));
237  }
238  bInComment = false;
239  } else {
240  name.prepend(c); // all comment stuff is part of the name
241  }
242  } else if (bInQuotesOutsideOfEmail) {
243  if (QLatin1Char(cQuotes) == c) {
244  bInQuotesOutsideOfEmail = false;
245  } else if (c != QLatin1Char('\\')) {
246  name.prepend(c);
247  }
248  } else {
249  // found the start of this addressee ?
250  if (QLatin1Char(',') == c) {
251  break;
252  }
253  // stuff is before the leading '<' ?
254  if (iMailStart) {
255  if (QLatin1Char(cQuotes) == c) {
256  bInQuotesOutsideOfEmail = true; // end of quoted text found
257  } else {
258  name.prepend(c);
259  }
260  } else {
261  switch (c.toLatin1()) {
262  case '<':
263  iMailStart = i;
264  break;
265  case ')':
266  if (!name.isEmpty()) {
267  name.prepend(QLatin1Char(' '));
268  }
269  bInComment = true;
270  break;
271  default:
272  if (QLatin1Char(' ') != c) {
273  mail.prepend(c);
274  }
275  }
276  }
277  }
278  }
279 
280  name = name.simplified();
281  mail = mail.simplified();
282 
283  if (mail.isEmpty()) {
284  return false;
285  }
286 
287  mail.append(QLatin1Char('@'));
288 
289  // Loop forward until we find the end of the string
290  // or a ',' that is outside of a comment
291  // and outside of quoted text behind the trailing '>'.
292  bInComment = false;
293  bInQuotesOutsideOfEmail = false;
294  int parenthesesNesting = 0;
295  for (i = iAd + 1; len > i; ++i) {
296  c = aStr[i];
297  if (bInComment) {
298  if (QLatin1Char(')') == c) {
299  if (--parenthesesNesting == 0) {
300  bInComment = false;
301  if (!name.isEmpty()) {
302  name.append(QLatin1Char(' '));
303  }
304  } else {
305  // nested ")", add it
306  name.append(QLatin1Char(')')); // name can't be empty here
307  }
308  } else {
309  if (QLatin1Char('(') == c) {
310  // nested "("
311  ++parenthesesNesting;
312  }
313  name.append(c); // all comment stuff is part of the name
314  }
315  } else if (bInQuotesOutsideOfEmail) {
316  if (QLatin1Char(cQuotes) == c) {
317  bInQuotesOutsideOfEmail = false;
318  } else if (c != QLatin1Char('\\')) {
319  name.append(c);
320  }
321  } else {
322  // found the end of this addressee ?
323  if (QLatin1Char(',') == c) {
324  break;
325  }
326  // stuff is behind the trailing '>' ?
327  if (iMailEnd) {
328  if (QLatin1Char(cQuotes) == c) {
329  bInQuotesOutsideOfEmail = true; // start of quoted text found
330  } else {
331  name.append(c);
332  }
333  } else {
334  switch (c.toLatin1()) {
335  case '>':
336  iMailEnd = i;
337  break;
338  case '(':
339  if (!name.isEmpty()) {
340  name.append(QLatin1Char(' '));
341  }
342  if (++parenthesesNesting > 0) {
343  bInComment = true;
344  }
345  break;
346  default:
347  if (QLatin1Char(' ') != c) {
348  mail.append(c);
349  }
350  }
351  }
352  }
353  }
354  }
355 
356  name = name.simplified();
357  mail = mail.simplified();
358 
359  return !(name.isEmpty() || mail.isEmpty());
360 }
361 
363 {
364  QString email;
365  QString name;
366  extractEmailAddressAndName(fullName, email, name);
367  return Person(name, email);
368 }
369 
370 #include "moc_person.cpp"
static bool isValidEmail(const QString &email)
Returns true if person's email address is valid.
Definition: person.cpp:141
KCALENDARCORE_EXPORT QDataStream & operator<<(QDataStream &out, const KCalendarCore::Alarm::Ptr &)
Alarm serializer.
Definition: alarm.cpp:820
CaseInsensitive
Namespace for all KCalendarCore types.
Definition: alarm.h:36
void clear()
QString & prepend(QChar ch)
QAction * mail(const QObject *recvr, const char *slot, QObject *parent)
bool operator==(const Person &person) const
Compares this with person for equality.
Definition: person.cpp:63
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
Alarm deserializer.
Definition: alarm.cpp:833
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString simplified() const const
bool operator!=(const Person &person) const
Compares this with person for non-equality.
Definition: person.cpp:68
static Person fromFullName(const QString &fullName)
Constructs a person with name and email address taken from fullName.
Definition: person.cpp:362
void setEmail(const QString &email)
Sets the email address for this person to email.
Definition: person.cpp:132
KCODECS_EXPORT bool extractEmailAddressAndName(const QString &aStr, QString &mail, QString &name)
Person & operator=(const Person &person)
Sets this person equal to person.
Definition: person.cpp:73
bool isEmpty() const const
int length() const const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString name(StandardShortcut id)
Person()
Constructs a blank person.
Definition: person.cpp:44
Represents a person, by name and email address.
Definition: person.h:37
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Person &key)
Return a hash value for a Person argument.
Definition: person.cpp:147
virtual ~Person()
Destroys a person.
char toLatin1() const const
QString mid(int position, int n) const const
QString fullName(const PartType &type)
QString & append(QChar ch)
void setName(const QString &name)
Sets the name of the person to name.
Definition: person.cpp:127
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Sep 29 2023 03:54:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.