KMime

content.h
Go to the documentation of this file.
1/*
2 KMime, the KDE Internet mail/usenet news message library.
3 SPDX-FileCopyrightText: 2001 the KMime authors.
4 See file AUTHORS for details
5 SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
6 SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10/**
11 @file
12 This file is part of the API for handling @ref MIME data and
13 defines the Content class.
14
15 @brief
16 Defines the Content class.
17
18 @authors the KMime authors (see AUTHORS file),
19 Volker Krause <vkrause@kde.org>
20
21TODO: possible glossary terms:
22 content
23 encoding, transfer type, disposition, description
24 header
25 body
26 attachment
27 charset
28 article
29 string representation
30 broken-down object representation
31*/
32
33#pragma once
34
35#include "kmime_export.h"
36#include "contentindex.h"
37#include "util.h"
38#include "headers.h"
39
40#include <QByteArray>
41#include <QList>
42#include <QMetaType>
43#include <QSharedPointer>
44
45namespace KMime
46{
47
48class ContentPrivate;
49class Message;
50
51/**
52 @brief
53 A class that encapsulates @ref MIME encoded Content.
54
55 A Content object holds two representations of a content:
56 - the string representation: This is the content encoded as a string ready
57 for transport. Accessible through the encodedContent() method.
58 - the broken-down representation: This is the tree of objects (headers,
59 sub-Contents and (if present) the encapsulated message) that this Content is made of.
60 Accessible through methods like header(), contents() and bodyAsMessage().
61
62 The parse() function updates the broken-down representation of the Content
63 from its string representation. Calling it is necessary to access the
64 headers, sub-Contents or the encapsulated message of this Content.
65
66 The assemble() function updates the string representation of the Content
67 from its broken-down representation. Calling it is necessary for
68 encodedContent() to reflect any changes made to the broken-down representation of the Content.
69
70 There are two basic types of a Content:
71 - A leaf Content: This is a content that is neither a multipart content nor an encapsulated
72 message. Because of this, it will not have any children, it has no sub-contents
73 and is therefore a leaf content.
74 Only leaf contents have a body that is not empty, i.e. functions that operate
75 on the body, such as body(), size() and decodedContent(), will work only on
76 leaf contents.
77 - A non-leaf Content: This is a content that itself doesn't have any body, but that does have
78 sub-contents.
79 This is the case for contents that are of mimetype multipart/ or of mimetype
80 message/rfc822. In case of a multipart content, contents() will return the
81 multipart child contents. In case of an encapsulated message, the message
82 can be accessed with bodyAsMessage(), and contents() will have one entry
83 that is the message as well.
84 On a non-leaf content, body() will have an empty return value and other
85 functions working on the body will not work.
86 A call to parse() is required before the child multipart contents or the
87 encapsulated message is created.
88*/
89class KMIME_EXPORT Content
90{
91public:
92
93 /**
94 Describes a list of Content objects.
95 */
97
98 /**
99 Creates an empty Content object with a specified parent.
100 @param parent the parent Content object
101 @since 4.3
102 */
103 explicit Content(Content *parent = nullptr);
104
105 /**
106 Destroys this Content object.
107 */
108 virtual ~Content();
109
110 /**
111 Returns true if this Content object is not empty.
112 */
113 [[nodiscard]] bool hasContent() const;
114
115 /**
116 Sets the Content to the given raw data, containing the Content head and
117 body separated by two linefeeds.
118
119 This method operates on the string representation of the Content. Call
120 parse() if you want to access individual headers, sub-Contents or the
121 encapsulated message.
122
123 @note The passed data must not contain any CRLF sequences, only LF.
124 Use CRLFtoLF for conversion before passing in the data.
125
126 @param s is a QByteArray containing the raw Content data.
127 */
128 void setContent(const QByteArray &s);
129
130 /**
131 * Parses the Content.
132 *
133 * This means the broken-down object representation of the Content is
134 * updated from the string representation of the Content.
135 *
136 * Call this if you want to access or change headers, sub-Contents or the
137 * encapsulated message.
138 *
139 * @note Calling parse() twice will not work for multipart contents or for
140 * contents of which the body is an encapsulated message. The reason is that
141 * the first parse() will delete the body, so there is no body to work on for
142 * the second call of parse().
143 *
144 * @note Calling this will reset the message returned by bodyAsMessage(), as
145 * the message is re-parsed as well.
146 * Also, all old sub-contents will be deleted, so any old Content
147 * pointer will become invalid.
148 */
149 void parse();
150
151 /**
152 Returns whether this Content is frozen.
153 A frozen content is immutable, i.e. calling assemble() will never modify
154 its head or body, and encodedContent() will return the same data before
155 and after parsing.
156
157 @since 4.4.
158 @see setFrozen().
159 */
160 [[nodiscard]] bool isFrozen() const;
161
162 /**
163 Freezes this Content if @p frozen is true; otherwise unfreezes it.
164 @param frozen freeze content if @c true, otherwise unfreeze
165 @since 4.4
166 @see isFrozen().
167 */
168 void setFrozen(bool frozen = true);
169
170 /**
171 Generates the MIME content.
172 This means the string representation of this Content is updated from the
173 broken-down object representation.
174 Call this if you have made changes to the content, and want
175 encodedContent() to reflect those changes.
176
177 @note assemble() has no effect if the Content isFrozen(). You may want
178 to freeze, for instance, signed sub-Contents, to make sure they are kept
179 unmodified.
180
181 @note If this content is an encapsulated message, i.e. bodyIsMessage()
182 returns true, then calling assemble() will also assemble the message
183 returned by bodyAsMessage().
184
185 @warning assemble() may change the order of the headers, and other
186 details such as where folding occurs. This may break things like
187 signature verification, so you should *ONLY* call assemble() when you
188 have actually modified the content.
189 */
190 void assemble();
191
192 /**
193 Clears the content, deleting all headers and sub-Contents.
194 */
195 void clear();
196
197 /**
198 Removes all sub-Contents from this content. Deletes them if @p del is true.
199 This is different from calling removeContent() on each sub-Content, because
200 removeContent() will convert this to a single-part Content if only one
201 sub-Content is left. Calling clearContents() does NOT make this Content
202 single-part.
203
204 @param del Whether to delete the sub-Contents.
205 @see removeContent()
206 @since 4.4
207 */
208 void clearContents(bool del = true);
209
210 /**
211 Returns the Content header raw data.
212
213 @see setHead().
214 */
215 [[nodiscard]] QByteArray head() const;
216
217 /**
218 Sets the Content header raw data.
219
220 This method operates on the string representation of the Content. Call
221 parse() if you want to access individual headers.
222
223 @param head is a QByteArray containing the header data.
224
225 @see head().
226 */
227 void setHead(const QByteArray &head);
228
229 /**
230 * Returns all headers.
231 * @since 5.7
232 */
233 [[nodiscard]] QList<Headers::Base *> headers() const;
234
235 /**
236 Returns the first header of type @p type, if it exists. Otherwise returns
237 0. Note that the returned header may be empty.
238 @param type the header type to find
239 @since 4.2
240 */
241 Headers::Base *headerByType(const char *type) const;
242
243 /**
244 Returns the first header of type T, if it exists.
245 If the header does not exist and @p create is true, creates an empty header
246 and returns it. Otherwise returns 0.
247 Note that the returned header may be empty.
248 @param create Whether to create the header if it does not exist.
249 @since 4.4.
250
251 KDE5: BIC: FIXME: Why is the default argument false here? That is
252 inconsistent with the methods in KMime::Message!
253 */
254 template <typename T> T *header(bool create = false);
255 /**
256 Returns the first header of type @tparam T.
257
258 Can be @c nullptr if such a header doesn't exist.
259 @since 24.08
260 */
261 template <typename T> [[nodiscard]] T *header() const;
262
263 /**
264 Returns all @p type headers in the Content.
265 Take care that this result is not cached, so could be slow.
266 @param type the header type to find
267 @since 4.2
268 */
269 [[nodiscard]] QList<Headers::Base *> headersByType(const char *type) const;
270
271 /**
272 Sets the specified header to this Content.
273 Any previous header of the same type is removed.
274 If you need multiple headers of the same type, use appendHeader() or
275 prependHeader().
276
277 @param h The header to set.
278 @see appendHeader()
279 @see removeHeader()
280 @since 4.4
281 */
282 void setHeader(Headers::Base *h);
283
284 /**
285 Appends the specified header to the headers of this Content.
286 @param h The header to append.
287 @since 4.4
288 */
289 void appendHeader(Headers::Base *h);
290
291 /**
292 Searches for the first header of type @p type, and deletes it, removing
293 it from this Content.
294 @param type The type of the header to look for.
295 @return true if a header was found and removed.
296 */
297 bool removeHeader(const char *type);
298
299 /**
300 Searches for the first header of type @p T, and deletes it, removing
301 it from this Content.
302 @tparam T The type of the header to look for.
303 @return true if a header was found and removed.
304 */
305 template <typename T> bool removeHeader();
306
307 /**
308 @return true if this Content has a header of type @p type.
309 @param type The type of the header to look for.
310 */
311 // TODO probably provide hasHeader<T>() too.
312 [[nodiscard]] bool hasHeader(const char *type) const;
313
314 /**
315 Returns the Content-Type header.
316
317 @param create If true, create the header if it doesn't exist yet.
318 */
319 Headers::ContentType *contentType(bool create = true);
320 /**
321 Returns the Content-Type header.
322
323 Can be @c nullptr if the header doesn't exist.
324 @since 24.08
325 */
326 [[nodiscard]] const Headers::ContentType *contentType() const;
327
328 /**
329 Returns the Content-Transfer-Encoding header.
330
331 @param create If true, create the header if it doesn't exist yet.
332 */
334 /**
335 Returns the Content-Transfer-Encoding header.
336
337 Can be @c nullptr if the header doesn't exist.
338 @since 24.08
339 */
341
342 /**
343 Returns the Content-Disposition header.
344
345 @param create If true, create the header if it doesn't exist yet.
346 */
348 /**
349 Returns the Content-Disposition header.
350
351 Can be @c nullptr if the header doesn't exist.
352 @since 24.08
353 */
355
356 /**
357 Returns the Content-Description header.
358
359 @param create If true, create the header if it doesn't exist yet.
360 */
362 /**
363 Returns the Content-Description header.
364
365 Can be @c nullptr if the header doesn't exist.
366 @since 24.08
367 */
369
370 /**
371 Returns the Content-Location header.
372
373 @param create If true, create the header if it doesn't exist yet.
374 @since 4.2
375 */
377 /**
378 Returns the Content-Location header.
379
380 Can be @c nullptr if the header doesn't exist.
381 @since 24.08
382 */
383 [[nodiscard]] const Headers::ContentLocation *contentLocation() const;
384
385 /**
386 Returns the Content-ID header.
387 @param create if true, create the header if it does not exist yet.
388 @since 4.4
389 */
390 Headers::ContentID *contentID(bool create = true);
391 /**
392 Returns the Content-ID header.
393
394 Can be @c nullptr if the header doesn't exist.
395 @since 24.08
396 */
397 [[nodiscard]] const Headers::ContentID *contentID() const;
398
399 /**
400 Returns the size of the Content body after encoding.
401 (If the encoding is quoted-printable, this is only an approximate size.)
402 This will return 0 for multipart contents or for encapsulated messages.
403 */
404 [[nodiscard]] qsizetype size() const;
405
406 /**
407 Returns the size of this Content and all sub-Contents.
408 */
409 [[nodiscard]] qsizetype storageSize() const;
410
411 /**
412 Returns the Content body raw data.
413
414 Note that this will be empty for multipart contents or for encapsulated
415 messages, after parse() has been called.
416
417 @see setBody().
418 */
419 [[nodiscard]] QByteArray body() const;
420
421 /**
422 Sets the Content decoded body raw data.
423
424 This method operates on the string representation of the Content. Call
425 parse() if you want to access individual sub-Contents or the encapsulated
426 message.
427
428 @param body is a QByteArray containing the body data.
429
430 @note @p body is assumed to be decoded as far as the content transfer encoding
431 is concerned.
432
433 @see setEncodedBody()
434 */
435 void setBody(const QByteArray &body);
436
437 /**
438 Sets the Content body raw data encoded according to the content transfer encoding.
439
440 This method operates on the string representation of the Content. Call
441 parse() if you want to access individual sub-Contents or the encapsulated
442 message.
443
444 @param body is a QByteArray containing the body data.
445
446 @note @p body is assumed to be encoded as far as the content transfer encoding
447 is concerned.
448
449 @since 24.08
450
451 @see setBody()
452 */
453 void setEncodedBody(const QByteArray &body);
454
455 /**
456 Returns the MIME preamble.
457
458 @return a QByteArray containing the MIME preamble.
459
460 @since 4.9
461 */
462 [[nodiscard]] QByteArray preamble() const;
463
464 /**
465 Sets the MIME preamble.
466
467 @param preamble a QByteArray containing what will be used as the
468 MIME preamble.
469
470 @since 4.9
471 */
472
473 void setPreamble(const QByteArray &preamble);
474
475 /**
476 Returns the MIME preamble.
477
478 @return a QByteArray containing the MIME epilogue.
479
480 @since 4.9
481 */
482 [[nodiscard]] QByteArray epilogue() const;
483
484 /**
485 Sets the MIME preamble.
486
487 @param epilogue a QByteArray containing what will be used as the
488 MIME epilogue.
489
490 @since 4.9
491 */
492 void setEpilogue(const QByteArray &epilogue);
493
494 /**
495 Returns a QByteArray containing the encoded Content, including the
496 Content header and all sub-Contents.
497
498 If you make changes to the broken-down representation of the message, be
499 sure to first call assemble() before calling encodedContent(), otherwise
500 the result will not be up-to-date.
501
502 If this content is an encapsulated message, i.e. bodyIsMessage() returns
503 true, then encodedContent() will use the message returned by bodyAsMessage()
504 as the body of the result, calling encodedContent() on the message.
505
506 @param useCrLf If true, use @ref CRLF instead of @ref LF for linefeeds.
507 */
508 [[nodiscard]] QByteArray encodedContent(bool useCrLf = false) const;
509
510 /**
511 * Like encodedContent(), with the difference that only the body will be
512 * returned, i.e. the headers are excluded.
513 *
514 * @since 4.6
515 */
516 [[nodiscard]] QByteArray encodedBody() const;
517
518 /**
519 * Returns the decoded Content body.
520 *
521 * Note that this will be empty for multipart contents or for encapsulated
522 * messages, after parse() has been called.
523 */
524 // TODO: KDE5: BIC: Rename this to decodedBody(), since only the body is
525 // returned. In contrast, setContent() sets the head and the body! Also, try
526 // to make this const.
527 [[nodiscard]] QByteArray decodedContent() const;
528
529 /**
530 Returns the decoded text. Additional to decodedContent(), this also
531 applies charset decoding. If this is not a text Content, decodedText()
532 returns an empty QString.
533
534 @param trimText If true, then the decoded text will have all trailing
535 whitespace removed.
536 @param removeTrailingNewlines If true, then the decoded text will have
537 all consecutive trailing newlines removed.
538
539 The last trailing new line of the decoded text is always removed.
540
541 */
542 // TODO: KDE5: BIC: Convert to enums. Also, what if trimText = true but
543 // removeTrailingNewlines
544 // is false?
545 [[nodiscard]] QString decodedText(bool trimText = false,
546 bool removeTrailingNewlines = false) const;
547
548 /**
549 Sets the Content body to the given string using charset of the content type.
550
551 If the charset can not be found, the system charset is taken and the content
552 type header is changed to that charset. The charset of the content type
553 header should be set to a charset that can encode the given string before
554 calling this method.
555
556 This method does not set the content transfer encoding automatically, it
557 needs to be set to a suitable value that can encode the given string before
558 calling this method.
559
560 This method only makes sense for single-part contents, do not try to pass a
561 multipart body or an encapsulated message here, that wouldn't work.
562
563 @param s Unicode-encoded string.
564 */
565 void fromUnicodeString(const QString &s);
566
567 /**
568 Returns the first Content with mimetype text/.
569 */
570 [[nodiscard]] Content *textContent();
571 /**
572 Returns the first Content with MIME type text/.
573 Const overload of the above, the returned Content cannot be modified.
574 @since 24.08
575 */
576 [[nodiscard]] const Content *textContent() const;
577
578 /**
579 * Returns all attachments below this node, recursively.
580 * This does not include crypto parts, nodes of alternative or related
581 * multipart nodes, or the primary body part (see textContent()).
582 * @see KMime::isAttachment(), KMime::hasAttachment()
583 */
584 [[nodiscard]] QList<Content *> attachments() const;
585
586 /**
587 * For multipart contents, this will return a list of all multipart child
588 * contents. For contents that are of mimetype message/rfc822, this will
589 * return a list with one entry, and that entry is the encapsulated message,
590 * as it would be returned by bodyAsMessage().
591 */
592 [[nodiscard]] QList<Content *> contents() const;
593
594 /**
595 Appends a new sub-Content. If the sub-Content is already part of another
596 Content object, it is removed from there and its parent is updated.
597
598 @param content The new sub-Content.
599 @see prependContent()
600 @see takeContent()
601 @since 6.0
602 */
603 void appendContent(Content *content);
604 /**
605 Prepends a new sub-Content. If the sub-Content is already part of another
606 Content object, it is removed from there and its parent is updated.
607
608 @param content The new sub-Content.
609 @see appendContent()
610 @see takeContent()
611 @since 6.0
612 */
613 void prependContent(Content *content);
614
615 void replaceContent(Content *oldContent, Content *newContent);
616 /**
617 Removes the given sub-Content and, if that actually was a sub-content
618 returns that.
619
620 @param content The Content to remove. It is not deleted, ownership goes
621 back to the caller.
622
623 @see appendContent()
624 @see prependContent()
625 @see clearContents()
626 @since 6.0
627 */
628 Content *takeContent(Content *content);
629
630 /**
631 Changes the encoding of this Content to @p e. If the Content is binary,
632 this actually re-encodes the data to use the new encoding.
633
634 @param e The new encoding to use.
635 */
636 void changeEncoding(Headers::contentEncoding e);
637
638 /**
639 Returns the Content specified by the given index.
640 If the index does not point to a Content, 0 is returned. If the index
641 is invalid (empty), this Content is returned.
642
643 @param index The Content index.
644 */
645 [[nodiscard]] Content *content(const ContentIndex &index) const;
646
647 /**
648 Returns the ContentIndex for the given Content, or an invalid index
649 if the Content is not found within the hierarchy.
650 @param content the Content object to search.
651 */
652 [[nodiscard]] ContentIndex indexForContent(Content *content) const;
653
654 /**
655 Returns true if this is the top-level node in the MIME tree. The top-level
656 node is always a Message or NewsArticle. However, a node can be a Message
657 without being a top-level node when it is an encapsulated message.
658 */
659 [[nodiscard]] bool isTopLevel() const;
660
661 /**
662 * Sets a new parent to the Content and add to its contents list. If it
663 * already had a parent, it is removed from the old parents contents list.
664 * @param parent the new parent
665 * @since 4.3
666 */
667 void setParent(Content *parent);
668
669 /**
670 * Returns the parent content object, or 0 if the content doesn't have a
671 * parent.
672 * @since 4.3
673 */
674 [[nodiscard]] Content *parent();
675 [[nodiscard]] const Content *parent() const;
676
677 /**
678 * Returns the toplevel content object, 0 if there is no such object.
679 * @since 4.3
680 */
681 [[nodiscard]] Content *topLevel();
682 [[nodiscard]] const Content *topLevel() const;
683
684
685 /**
686 * Returns the index of this Content based on the topLevel() object.
687 * @since 4.3
688 */
689 [[nodiscard]] ContentIndex index() const;
690
691 /**
692 * @return true if this content is an encapsulated message, i.e. if it has the
693 * mimetype message/rfc822.
694 *
695 * @since 4.5
696 */
697 // AK_REVIEW: move to MessageViewer/ObjectTreeParser
698 [[nodiscard]] bool bodyIsMessage() const;
699
700 /**
701 * If this content is an encapsulated message, in which case bodyIsMessage()
702 * will return true, the message represented by the body of this content will
703 * be returned. The returned message is already fully parsed. Calling this
704 * method is the aquivalent of calling contents().first() and casting the
705 * result to a KMime::Message*. bodyAsMessage() has the advantage that it will
706 * return a shared pointer that will not be destroyed when the container
707 * message is destroyed or re-parsed.
708 *
709 * The message that is returned here is created when calling parse(), so make
710 * sure to call parse() first. Since each parse() creates a new message
711 * object, a different message object will be returned each time you call
712 * parse().
713 *
714 * If you make changes to the returned message, you need to call assemble() on
715 * this content or on the message if you want that encodedContent() reflects
716 * these changes. This also means that calling assemble() on this content will
717 * assemble the returned message.
718 *
719 * @since 4.5
720 */
721 // AK_REVIEW: move to MessageViewer/ObjectTreeParser
722 [[nodiscard]] QSharedPointer<Message> bodyAsMessage() const;
723
724protected:
725 /**
726 Reimplement this method if you need to assemble additional headers in a
727 derived class. Don't forget to call the implementation of the base class.
728 @return The raw, assembled headers.
729 */
730 virtual QByteArray assembleHeaders();
731
732 //@cond PRIVATE
733 ContentPrivate *d_ptr;
734 //@endcond
735
736private:
737 Q_DECLARE_PRIVATE(Content)
738 Q_DISABLE_COPY(Content)
739};
740
741template <typename T> T *Content::header(bool create)
742{
743 Headers::Base *h = headerByType(T::staticType());
744 if (h) {
745 // Make sure the header is actually of the right type.
746 Q_ASSERT(dynamic_cast<T *>(h));
747 } else if (create) {
748 h = new T;
749 appendHeader(h); // we already know the header doesn't exist yet
750 }
751 return static_cast<T *>(h);
752}
753
754template <typename T> T *Content::header() const
755{
756 Headers::Base *h = headerByType(T::staticType());
757 if (h) {
758 // Make sure the header is actually of the right type.
759 Q_ASSERT(dynamic_cast<T *>(h));
760 }
761 return static_cast<T *>(h);
762}
763
764template <typename T> bool Content::removeHeader()
765{
766 return removeHeader(T::staticType());
767}
768
769} // namespace KMime
770
771Q_DECLARE_METATYPE(KMime::Content*)
772
A class to uniquely identify message parts (Content) in a hierarchy.
A class that encapsulates MIME encoded Content.
Definition content.h:90
Headers::ContentDescription * contentDescription(bool create=true)
Returns the Content-Description header.
bool removeHeader()
Searches for the first header of type T, and deletes it, removing it from this Content.
Definition content.h:764
Headers::ContentType * contentType(bool create=true)
Returns the Content-Type header.
const Headers::ContentType * contentType() const
Returns the Content-Type header.
const Headers::ContentLocation * contentLocation() const
Returns the Content-Location header.
T * header() const
Returns the first header of type.
Definition content.h:754
Headers::ContentTransferEncoding * contentTransferEncoding(bool create=true)
Returns the Content-Transfer-Encoding header.
Headers::ContentDisposition * contentDisposition(bool create=true)
Returns the Content-Disposition header.
QList< KMime::Content * > List
Describes a list of Content objects.
Definition content.h:96
const Headers::ContentID * contentID() const
Returns the Content-ID header.
const Headers::ContentTransferEncoding * contentTransferEncoding() const
Returns the Content-Transfer-Encoding header.
Headers::ContentID * contentID(bool create=true)
Returns the Content-ID header.
Headers::Base * headerByType(const char *type) const
Returns the first header of type type, if it exists.
Definition content.cpp:563
const Headers::ContentDisposition * contentDisposition() const
Returns the Content-Disposition header.
Headers::ContentLocation * contentLocation(bool create=true)
Returns the Content-Location header.
void appendHeader(Headers::Base *h)
Appends the specified header to the headers of this Content.
Definition content.cpp:597
const Headers::ContentDescription * contentDescription() const
Returns the Content-Description header.
Baseclass of all header-classes.
Definition headers.h:97
Represents a "Content-Description" header.
Definition headers.h:1247
Represents a "Content-Disposition" header.
Definition headers.h:1149
Represents a "Content-ID" header.
Definition headers.h:944
Represents a "Content-Location" header.
Definition headers.h:1256
Represents a "Content-Transfer-Encoding" header.
Definition headers.h:872
Represents a "Content-Type" header.
Definition headers.h:992
This file is part of the API for handling MIME data and defines the ContentIndex class.
This file is part of the API for handling MIME data and defines the various header classes:
contentEncoding
Various possible values for the "Content-Transfer-Encoding" header.
Definition headers.h:52
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:51:33 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.