File xeme-0+53.obscpio of Package xeme

07070100000000000081A400000000000000000000000167AEF5110000005C000000000000000000000000000000000000001400000000xeme-0+53/.hgignoresyntax: glob
*.orig
*-docs.zip

syntax: regexp
^build.*\/
^po\/.*\.pot$
^subprojects\/.+\/

07070100000001000081A400000000000000000000000167AEF511000000AC000000000000000000000000000000000000001900000000xeme-0+53/.reviewboardrcREVIEWBOARD_URL = "https://reviews.imfreedom.org/"
REPOSITORY = "xeme/xeme"
REPOSITORY_TYPE = "mercurial"
BRANCH = "default"
LAND_DEST_BRANCH = "default"
LAND_PUSH = False
07070100000002000081A400000000000000000000000167AEF5110000005A000000000000000000000000000000000000001200000000xeme-0+53/AUTHORSElliott Sales de Andrade <quantum.analyst@gmail.com>
Gary Kramlich <grim@reaperworld.com>
07070100000003000081A400000000000000000000000167AEF511000067A2000000000000000000000000000000000000001200000000xeme-0+53/LICENSE                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!
07070100000004000081A400000000000000000000000167AEF511000005CE000000000000000000000000000000000000001400000000xeme-0+53/README.md# Xeme

A high level XMPP parsing library based on GObjects.

## About

This project grew out of necessity for [Pidgin 3](https://pidgin.im). We needed
to modernize our XMPP support but we also have Bonjour support which uses the
XMPP standards for sending messages. This meant we needed something reuseable
between the two, and thus Xeme was born.

It's meant to be a standalone library so that others can use it as well. Its
goal is to handle all of the marshaling and unmarshaling for you and give you
high level XMPP stanza objects to work with.

## Name

The name Xeme comes from one of the alternate scientific names of
[Sabine's Gull](https://birdsoftheworld.org/bow/species#larida1:sabgul). As
this project is related to Pidgin, it is only fitting that it got a bird name.
But since it is an XMPP project as well, it need an X. To top everything off,
the name is very close to meme, which get shared via chat networks. This level
of criteria is fitting for an open source project, so we went with it!

## Building

Standard [meson](https://mesonbuild.com) project build:

```sh
meson setup build
meson compile -C build
meson install -C build
```

### Configuration

There are configuration options for a few things, but you'll probably only be
interested in `doc` and `introspection`. Please note that `doc` requires
`introspection`.

## Documentation

Documentation and API reference for the project can be found at
[docs.imfreedom.org/xeme](https://docs.imfreedom.org/xeme/).
07070100000005000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000001100000000xeme-0+53/convey07070100000006000081A400000000000000000000000167AEF511000005AE000000000000000000000000000000000000001600000000xeme-0+53/convey.yamlenvironment:
  - BUILD_NUMBER
  - REPOSITORY=rwgrim/xeme-builders
  - REGISTRY=docker.io
  - REGISTRY_USERNAME
  - REGISTRY_PASSWORD
  - DOCS_BUILD_IMAGE=${REGISTRY}/${REPOSITORY}:debian-trixie-amd64

tasks:
  clean:
    type: convey/clean
    files:
      - debian-trixie-amd64

  import:
    type: docker/import
    files: .:.

  build:
    type: docker/run
    image: ${REGISTRY}/${REPOSITORY}:${TARGET}

  export:
    type: docker/export
    files: ${TARGET}

  login:
    type: docker/login
    server: ${REGISTRY}
    username: ${REGISTRY_USERNAME}
    password: ${REGISTRY_PASSWORD}

  logout:
    type: docker/logout
    server: ${REGISTRY}

  docs-clean:
    type: convey/clean
    files:
      - xeme-docs.zip
  docs-build:
    type: docker/run
    image: ${DOCS_BUILD_IMAGE}
    workdir: ${CONVEY_WORKSPACE}
    script:
      - set -ex
      - rm -rf build-docs
      - meson setup build-docs -Ddoc=true
      - ninja -C build-docs doc
      - cd build-docs/xeme/reference
      - zip -9r xeme-docs.zip xeme
  docs-export:
    type: docker/export
    files:
      - build-docs/xeme/reference/xeme-docs.zip:xeme-docs.zip

plans:
  clean:
    stages:
      - tasks:
          - clean
          - docs-clean

  debian-trixie-amd64:
    environment: TARGET=debian-trixie-amd64
    stages:
      - tasks: [import, build]
      - tasks: [export]
        run: always

  docs:
    stages:
      - tasks: [docs-clean, import, docs-build, docs-export]

07070100000007000081A400000000000000000000000167AEF511000001E7000000000000000000000000000000000000003000000000xeme-0+53/convey/Dockerfile.debian-trixie-amd64FROM docker.io/debian:trixie-slim

RUN set -ex && \
	apt-get update && \
	apt-get install -y --no-install-recommends \
		build-essential \
		ca-certificates \
		gettext \
		git \
		gobject-introspection \
		libgirepository1.0-dev \
		libglib2.0-dev \
		mercurial \
		meson \
		python3-jinja2 \
		python3-packaging \
		python3-pygments \
		python3-toml \
		python3-typogrify \
		zip \
	&& \
	apt-get clean && \
	rm -rf /var/lib/apt/lists

COPY meson-build.sh /build.sh

CMD ["/build.sh"]
07070100000008000081A400000000000000000000000167AEF51100000439000000000000000000000000000000000000001D00000000xeme-0+53/convey/convey.yaml---
environment:
  - REPO=rwgrim/xeme-builders
  - REGISTRY=docker.io
  - REGISTRY_USERNAME
  - REGISTRY_PASSWORD
tasks:
  build:
    type: docker/build
    dockerfile: Dockerfile.${TARGET}
    tag: ${REGISTRY}/${REPO}:${TARGET}
    files: .:.
    labels:
      - COMMIT=${HG_COMMIT}

  clean:
    type: docker/remove
    quiet: true
    images:
      - ${REGISTRY}/${REPO}/${TARGET}

  import:
    type: docker/import
    files:
      - .:.

  login:
    type: docker/login
    username: ${REGISTRY_USERNAME}
    password: ${REGISTRY_PASSWORD}
    server: ${REGISTRY}

  logout:
    type: docker/logout
    server: ${REGISTRY}

  publish:
    type: docker/push
    image: ${REGISTRY}/${REPO}:${TARGET}

plans:
  clean:
    stages:
      - tasks: clean

  build:
    stages:
      - tasks: [import, build]

  publish:
    environment: [REGISTRY_USERNAME, REGISTRY_PASSWORD]
    stages:
      - tasks: [login, publish]
      - tasks: [logout]
        run: always

  debian-trixie-amd64:
    environment:
      - TARGET=debian-trixie-amd64
    stages:
      - tasks: [import, build]
07070100000009000081ED00000000000000000000000167AEF511000000A7000000000000000000000000000000000000002000000000xeme-0+53/convey/meson-build.sh#!/bin/sh -ex

cd ${CONVEY_WORKSPACE}

BUILD_DIR=${TARGET:-_build}

meson ${MESON_OPTIONS} "${BUILD_DIR}"
meson compile -C "${BUILD_DIR}"
meson test -C "${BUILD_DIR}"
0707010000000A000081A400000000000000000000000167AEF51100000E9E000000000000000000000000000000000000001600000000xeme-0+53/meson.buildproject('xeme', 'C',
  default_options : ['c_std=c17', 'warning_level=3'],
  license : 'LGPL-2.1-or-later',
  meson_version : '>=1.0.0',
  version : '0.1.0')

toplevel_inc = include_directories('.')

gnome = import('gnome')
pkgconfig = import('pkgconfig')

###############################################################################
# Versioning
###############################################################################
parts = meson.project_version().split('-')
if parts.length() > 1
    extra = parts[1]
else
    extra = ''
endif

parts = parts[0].split('.')
XEME_MAJOR_VERSION = parts[0]
XEME_MINOR_VERSION = parts[1]
XEME_MICRO_VERSION = parts[2]
XEME_LIB_VERSION = f'@XEME_MAJOR_VERSION@.@XEME_MINOR_VERSION@.@XEME_MICRO_VERSION@'

version_config = configuration_data()
version_config.set('XEME_MAJOR_VERSION', XEME_MAJOR_VERSION)
version_config.set('XEME_MINOR_VERSION', XEME_MINOR_VERSION)
version_config.set('XEME_MICRO_VERSION', XEME_MICRO_VERSION)
version_config.set('XEME_EXTRA_VERSION', extra)
version_config.set('XEME_VERSION', meson.project_version())

###############################################################################
# Dependencies
###############################################################################
glib_dep = dependency('glib-2.0', version : '>=2.76.0')
gio_dep = dependency('gio-2.0')
gobject_dep = dependency('gobject-2.0')
birb_dep = dependency('birb')

add_project_arguments(
  '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_76',
  '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_76',
  language : 'c',)

###############################################################################
# Internationalization
###############################################################################
GETTEXT_PACKAGE = 'xeme'
LOCALE_DIR = get_option('prefix') / get_option('localedir')

add_project_arguments(f'-DLOCALEDIR="@LOCALE_DIR@"', language : 'c')
add_project_arguments(f'-DGETTEXT_PACKAGE="@GETTEXT_PACKAGE@"', language : 'c')

###############################################################################
# Documentation
###############################################################################
if get_option('doc') and not get_option('introspection')
  error('Documentation requires GObject Introspection.')
endif

gidocgen_dep = dependency(
  'gi-docgen', version: '>= 2023.1',
  fallback: ['gi-docgen', 'dummy_dep'],
  required: get_option('doc')
)

gidocgen = find_program('gi-docgen', required : get_option('doc'))
docs_dir = get_option('prefix') / get_option('datadir') / 'doc'
doc_targets = []

###############################################################################
# Sub directories
###############################################################################
subdir('xeme')
subdir('po')

###############################################################################
# Custom Targets
###############################################################################
if meson.backend() == 'ninja'
  run_target('turtles',
    command : ['ninja', '-C', '@BUILD_ROOT@', 'xeme-pot', 'all', 'test'])
endif

# This needs to be after all of the sub-directories have been processed.
if get_option('doc')
  alias_target('doc', doc_targets)
endif

###############################################################################
# Summarize all the things!
###############################################################################
summary({
  'prefix': get_option('prefix'),
  'bindir': get_option('bindir'),
  'libdir': get_option('libdir'),
  'datadir': get_option('datadir'),
}, section : 'Directories')

summary({
  'documentation': get_option('doc'),
  'internationalization': get_option('nls'),
  'introspection': get_option('introspection'),
}, section : 'Options')
0707010000000B000081A400000000000000000000000167AEF5110000018B000000000000000000000000000000000000001C00000000xeme-0+53/meson_options.txtoption(
  'doc',
  type : 'boolean', value : true, yield : true,
  description : 'build documentation with gi-docgen'
)

option(
  'introspection',
  type : 'boolean', value : true, yield : true,
  description : 'Whether or not to build a GObject Introspection type library'
)

option(
    'nls',
    type : 'boolean', value : true, yield : true,
    description : 'Install translation files'
)
0707010000000C000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000000D00000000xeme-0+53/po0707010000000D000081A400000000000000000000000167AEF51100000000000000000000000000000000000000000000001500000000xeme-0+53/po/LINGUAS0707010000000E000081A400000000000000000000000167AEF51100000054000000000000000000000000000000000000001600000000xeme-0+53/po/POTFILESxeme/xemeinputstream.c
xeme/xememessage.c
xeme/xemeoutputstream.c
xeme/xemestanza.c
0707010000000F000081A400000000000000000000000167AEF51100000119000000000000000000000000000000000000001900000000xeme-0+53/po/meson.buildif not get_option('nls')
	summary('translations',
		'You have disabled installation of translations which means ' +
		'English will be the only available language.',
		section : 'Warnings')

	subdir_done()
endif

i18n = import('i18n')
i18n.gettext(GETTEXT_PACKAGE, preset: 'glib')
07070100000010000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000001600000000xeme-0+53/subprojects07070100000011000081A400000000000000000000000167AEF5110000005F000000000000000000000000000000000000002000000000xeme-0+53/subprojects/birb.wrap[wrap-hg]
directory = birb
url = https://keep.imfreedom.org/birb/birb/
revision = 50a501129d04
07070100000012000081A400000000000000000000000167AEF511000000F9000000000000000000000000000000000000002500000000xeme-0+53/subprojects/gi-docgen.wrap[wrap-file]
directory = gi-docgen-2023.1
source_url = https://download.gnome.org/sources/gi-docgen/2023/gi-docgen-2023.1.tar.xz
source_filename = gi-docgen-2023.1.tar.xz
source_hash = a9a687c1b7c4a4139a214bd451e01ca86131a3161f68aa3e07325b06002bbfb6
07070100000013000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000000F00000000xeme-0+53/xeme07070100000014000081A400000000000000000000000167AEF5110000108B000000000000000000000000000000000000001B00000000xeme-0+53/xeme/meson.buildXEME_SOURCES = [
  'xemeaddress.c',
  'xemeconnection.c',
  'xemeextension.c',
  'xemeextensionmap.c',
  'xemeinputstream.c',
  'xememessage.c',
  'xemeoutputstream.c',
  'xemestanza.c',
  'xemestream.c',
  'xemeversion.c',
]

XEME_HEADERS = [
  'xemeaddress.h',
  'xemeconnection.h',
  'xemeconstants.h',
  'xemecore.h',
  'xemeextension.h',
  'xemeextensionmap.h',
  'xemeinputstream.h',
  'xememessage.h',
  'xemeoutputstream.h',
  'xemestanza.h',
  'xemestream.h',
  'xemestring.h',
  'xemeversion.h',
]

XEME_BUILT_HEADERS = []
XEME_BUILT_SOURCES = []
XEME_GENERATED_TARGETS = []

###############################################################################
# xemeversionconsts.h
###############################################################################
xeme_version_consts_h = configure_file(
    input : 'xemeversionconsts.h.in',
    output : 'xemeversionconsts.h',
    configuration : version_config,
    install : true,
    install_dir : get_option('includedir') / 'xeme-1.0' / 'xeme')
XEME_BUILT_HEADERS += xeme_version_consts_h

###############################################################################
# Single Header
###############################################################################
XEME_H_INCLUDES = []
foreach header : XEME_HEADERS
    XEME_H_INCLUDES += f'#include <xeme/@header@>'
endforeach

header_config = configuration_data()
header_config.set('XEME_H_INCLUDES', '\n'.join(XEME_H_INCLUDES))
xeme_h = configure_file(
    input : 'xeme.h.in',
    output : 'xeme.h',
    configuration : header_config,
    install : true,
    install_dir : get_option('includedir') / 'xeme-1.0')

XEME_BUILT_HEADERS += xeme_h

###############################################################################
# Enums
###############################################################################
XEME_ENUM_HEADERS = [
  'xemeconnection.h',
]

xeme_enums = gnome.mkenums_simple(
  'xemeenums',
  sources : XEME_ENUM_HEADERS,
  install_header : true,
  install_dir : get_option('includedir') / 'xeme-1.0' / 'xeme')

XEME_BUILT_SOURCES += xeme_enums[0]
XEME_BUILT_HEADERS += xeme_enums[1]

###############################################################################
# Library Target
###############################################################################
xeme_inc = include_directories('.')

xeme_lib = library('xeme',
  XEME_SOURCES + XEME_HEADERS + XEME_BUILT_SOURCES + XEME_BUILT_HEADERS,
  c_args : ['-DXEME_COMPILATION', '-DG_LOG_USE_STRUCTURED', '-DG_LOG_DOMAIN="Xeme"'],
  gnu_symbol_visibility : 'hidden',
  dependencies : [birb_dep, gio_dep, glib_dep, gobject_dep],
  include_directories : [toplevel_inc, xeme_inc],
  version : XEME_LIB_VERSION,
  install : true)

install_headers(
  XEME_HEADERS,
  subdir : 'xeme-1.0/xeme')

pkgconfig.generate(
  xeme_lib,
  name : 'xeme',
  description : 'Xeme High Level XMPP Parsing Library',
  filebase : 'xeme',
  subdirs : 'xeme-1.0',
  libraries : [birb_dep, gio_dep, glib_dep, gobject_dep],
)

###############################################################################
# GObject Introspection
###############################################################################
if get_option('introspection')
  xeme_gir = gnome.generate_gir(xeme_lib,
    sources : XEME_SOURCES + XEME_HEADERS,
    includes : ['Gio-2.0', 'GObject-2.0'],
    header : 'xeme.h',
    namespace : 'Xeme',
    symbol_prefix : 'xeme',
    nsversion : '1.0',
    install : true,
    export_packages : ['xeme'],
    extra_args : ['--quiet', '-DXEME_COMPILATION'])
  XEME_GENERATED_TARGETS += xeme_gir
endif

###############################################################################
# Library Dependency Object
###############################################################################
xeme_dep = declare_dependency(
  dependencies : [gio_dep, glib_dep, gobject_dep],
  include_directories : [toplevel_inc, xeme_inc],
  link_with : xeme_lib,
  sources : XEME_BUILT_HEADERS + XEME_GENERATED_TARGETS)

meson.override_dependency('xeme', xeme_dep)

###############################################################################
# Sub Directories
###############################################################################
subdir('reference')
subdir('tests')
07070100000015000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000001900000000xeme-0+53/xeme/reference07070100000016000081A400000000000000000000000167AEF5110000036F000000000000000000000000000000000000002500000000xeme-0+53/xeme/reference/meson.buildif not get_option('doc')
  subdir_done()
endif

xeme_doc_content_files = []

xeme_gidocgen_toml = configure_file(
  input : 'xeme.toml.in',
  output : 'xeme.toml',
  configuration : version_config,
  install : true,
  install_dir : docs_dir / 'xeme')

xeme_doc = custom_target('xeme-doc',
  input : [ xeme_gidocgen_toml, xeme_gir[0] ],
  output : 'xeme',
  command : [
    gidocgen,
    'generate',
    '--quiet',
    '--fatal-warnings',
    '--config=@INPUT0@',
    '--output-dir=@OUTPUT@',
    '--no-namespace-dir',
    '--content-dir=@0@'.format(meson.current_source_dir()),
    '@INPUT1@'
  ],
  depend_files : [ xeme_doc_content_files ],
  build_by_default : true,
  install : true,
  install_dir : docs_dir,
)

test('doc-check',
  gidocgen,
  args: [
    'check',
    '--config', xeme_gidocgen_toml,
    xeme_gir[0],
  ],
  depends: xeme_gir[0],
)

doc_targets += xeme_doc
07070100000017000081A400000000000000000000000167AEF51100000146000000000000000000000000000000000000002300000000xeme-0+53/xeme/reference/urlmap.js// SPDX-FileCopyrightText: 2021 GNOME Foundation
// SPDX-License-Identifier: LGPL-2.1-or-later

// A map between namespaces and base URLs for their online documentation
baseURLs = [
    [ 'Gio', 'https://docs.gtk.org/gio/' ],
    [ 'GLib', 'https://docs.gtk.org/glib/' ],
    [ 'GObject', 'https://docs.gtk.org/gobject/' ],
]
07070100000018000081A400000000000000000000000167AEF51100000598000000000000000000000000000000000000002600000000xeme-0+53/xeme/reference/xeme.toml.in[library]
version = "@XEME_VERSION@"
browse_url = "https://keep.imfreedom.org/xeme/xeme/"
repository_url = "https://keep.imfreedom.org/xeme/xeme/"
website_url = "https://keep.imfreedom.org/xeme/xeme/"
authors = "Xeme Developers"
logo_url = ""
license = "LGPL-2.1-or-later"
description = "Xeme High Level XMPP Parsing Library"
dependencies = [ "GLib-2.0", "GObject-2.0" ]
devhelp = true
search_index = true

  [dependencies."Gio-2.0"]
  name = "Gio"
  description = "Stream based io library."
  docs_url = "https://docs.gtk.org/gio/"

  [dependencies."GLib-2.0"]
  name = "GLib"
  description = "General-purpose, portable utility library."
  docs_url = "https://docs.gtk.org/glib/"

  [dependencies."GObject-2.0"]
  name = "GObject"
  description = "The base type system library."
  docs_url = "https://docs.gtk.org/gobject/"

[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true

[source-location]
base_url = "https://keep.imfreedom.org/xeme/xeme/file/default/"

[extra]
# The same order will be used when generating the index
content_files = []
content_images = []
urlmap_file = "urlmap.js"

[[object]]
pattern = "DEPRECATED_IN_*"
hidden = true

[[object]]
name = "DEPRECATED_FOR"
hidden = true

[[object]]
name = "UNAVAILABLE"
hidden = true

[[object]]
name = "UNAVAILABLE_MACRO"
hidden = true

[[object]]
name = "UNAVAILABLE_STATIC_INLINE"
hidden = true

[[object]]
name = "UNAVAILABLE_TYPE"
hidden = true07070100000019000041ED00000000000000000000000267AEF51100000000000000000000000000000000000000000000001500000000xeme-0+53/xeme/tests0707010000001A000081A400000000000000000000000167AEF51100000116000000000000000000000000000000000000002100000000xeme-0+53/xeme/tests/meson.buildPROGRAMS = [
  'address',
  'extension',
  'extensionmap',
  'inputstream',
  'message',
  'outputstream',
  'stanza',
  'stream',
]

foreach PROGRAM : PROGRAMS
  e = executable(f'test@PROGRAM@', f'test@PROGRAM@.c',
    dependencies : [xeme_dep])

  test(PROGRAM, e)
endforeach
0707010000001B000081A400000000000000000000000167AEF51100000A9C000000000000000000000000000000000000002300000000xeme-0+53/xeme/tests/testaddress.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_address_valid(void) {
	const char *data[] = {
		"gmail.com",
		"gmail.com/Test",
		"gmail.com/Test@",
		"gmail.com/@",
		"gmail.com/Test@alkjaweflkj",
		"noone@example.com",
		"noone@example.com/Test12345",
		"noone@example.com/Test@12345",
		"noone@example.com/Te/st@12@//345",
		"わいど@conference.jabber.org",
		"まりるーむ@conference.jabber.org",
		"noone@example.com/まりるーむ",
		"noone@example/stuff.org",
		"noone@nödåtXäYZ.example",
		"noone@nödåtXäYZ.example/まりるーむ",
		"noone@わいど.org",
		"noone@まつ.おおかみ.net",
		"noone@310.0.42.230/s",
		"noone@[::1]", /* IPv6 */
		"noone@[3001:470:1f05:d58::2]",
		"noone@[3001:470:1f05:d58::2]/foo",
		"no=one@310.0.42.230",
		"no,one@310.0.42.230",
		NULL,
	};

	for(int i = 0; data[i] != NULL; i++) {
		g_assert_true(xeme_address_parse(data[i], NULL, NULL, NULL, NULL));
	}
}

static void
test_xeme_address_invalid(void) {
	const gchar *data[] = {
		"@gmail.com",
		"@@gmail.com",
		"noone@@example.com/Test12345",
		"no@one@example.com/Test12345",
		"@example.com/Test@12345",
		"/Test@12345",
		"noone@",
		"noone/",
		"noone@gmail_stuff.org",
		"noone@gmail[stuff.org",
		"noone@gmail\\stuff.org",
		"noone@[::1]124",
		"noone@2[::1]124/as",
		NULL,
	};

	for(int i = 0; data[i] != NULL; i++) {
		g_assert_false(xeme_address_parse(data[i], NULL, NULL, NULL, NULL));
	}
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/xeme/address/valid", test_xeme_address_valid);
	g_test_add_func("/xeme/address/invalid", test_xeme_address_invalid);

	return g_test_run();
}
0707010000001C000081A400000000000000000000000167AEF5110000179B000000000000000000000000000000000000002500000000xeme-0+53/xeme/tests/testextension.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_extension_properties(void) {
	XemeExtension *extension = NULL;
	char *element = NULL;
	char *namespace = NULL;

	extension = g_object_new(
		XEME_TYPE_EXTENSION,
		"element", "element1",
		"namespace", "namespace1",
		NULL);

	g_object_get(
		G_OBJECT(extension),
		"element", &element,
		"namespace", &namespace,
		NULL);

	g_assert_cmpstr(element, ==, "element1");
	g_clear_pointer(&element, g_free);

	g_assert_cmpstr(namespace, ==, "namespace1");
	g_clear_pointer(&namespace, g_free);

	g_assert_finalize_object(extension);
}

/******************************************************************************
 * Normal Extension
 *****************************************************************************/
G_DECLARE_FINAL_TYPE(TestXemeExtensionNormal, test_xeme_extension_normal,
                     TEST_XEME, EXTENSION_NORMAL, XemeExtension)

struct _TestXemeExtensionNormal {
	XemeExtension parent;

	guint parse_start;
	guint parse_end;
	guint serialize;
};

G_DEFINE_FINAL_TYPE(TestXemeExtensionNormal, test_xeme_extension_normal,
                    XEME_TYPE_EXTENSION)

static void
test_xeme_extension_normal_parse_start(XemeExtension *extension,
                                       XemeInputStream *input,
                                       GMarkupParseContext *context)
{
	TestXemeExtensionNormal *normal = NULL;

	g_assert_true(TEST_XEME_IS_EXTENSION_NORMAL(extension));
	g_assert_true(XEME_IS_INPUT_STREAM(input));
	g_assert_nonnull(context);

	normal = TEST_XEME_EXTENSION_NORMAL(extension);

	normal->parse_start += 1;
}


static void
test_xeme_extension_normal_parse_end(XemeExtension *extension,
                                     GMarkupParseContext *context)
{
	TestXemeExtensionNormal *normal = NULL;

	g_assert_true(TEST_XEME_IS_EXTENSION_NORMAL(extension));
	g_assert_nonnull(context);

	normal = TEST_XEME_EXTENSION_NORMAL(extension);

	normal->parse_end += 1;
}

static char *
test_xeme_extension_normal_serialize(XemeExtension *extension) {
	TestXemeExtensionNormal *normal = NULL;

	g_assert_true(XEME_IS_EXTENSION(extension));
	g_assert_true(TEST_XEME_IS_EXTENSION_NORMAL(extension));

	normal = TEST_XEME_EXTENSION_NORMAL(extension);

	normal->serialize += 1;

	return g_strdup("serialized");
}

static void
test_xeme_extension_normal_init(TestXemeExtensionNormal *extension) {
	extension->parse_start = 0;
	extension->parse_end = 0;
	extension->serialize = 0;
}

static void
test_xeme_extension_normal_class_init(TestXemeExtensionNormalClass *klass) {
	XemeExtensionClass *extension_class = XEME_EXTENSION_CLASS(klass);

	extension_class->parse_start = test_xeme_extension_normal_parse_start;
	extension_class->parse_end = test_xeme_extension_normal_parse_end;
	extension_class->serialize = test_xeme_extension_normal_serialize;
}

/******************************************************************************
 * Normal Tests
 *****************************************************************************/
static void
test_xeme_extension_normal_parse_test(void) {
	TestXemeExtensionNormal *extension = NULL;
	XemeInputStream *input = NULL;
	GMarkupParseContext *context = NULL;
	GMarkupParser parser = {
		.text = NULL,
	};

	context = g_markup_parse_context_new(&parser, G_MARKUP_DEFAULT_FLAGS,
	                                     NULL, NULL);

	extension = g_object_new(test_xeme_extension_normal_get_type(), NULL);

	input = xeme_input_stream_new();

	xeme_extension_parse_start(XEME_EXTENSION(extension), input, context);
	g_assert_cmpuint(extension->parse_start, ==, 1);
	g_assert_cmpuint(extension->parse_end, ==, 0);
	g_assert_cmpuint(extension->serialize, ==, 0);

	xeme_extension_parse_end(XEME_EXTENSION(extension), context);
	g_assert_cmpuint(extension->parse_start, ==, 1);
	g_assert_cmpuint(extension->parse_end, ==, 1);
	g_assert_cmpuint(extension->serialize, ==, 0);

	g_markup_parse_context_free(context);

	g_assert_finalize_object(extension);
	g_assert_finalize_object(input);
}

static void
test_xeme_extension_normal_serialize_test(void) {
	TestXemeExtensionNormal *extension = NULL;
	char *output = NULL;

	extension = g_object_new(test_xeme_extension_normal_get_type(), NULL);
	output = xeme_extension_serialize(XEME_EXTENSION(extension));

	g_assert_cmpstr(output, ==, "serialized");
	g_clear_pointer(&output, g_free);

	g_assert_cmpuint(extension->parse_start, ==, 0);
	g_assert_cmpuint(extension->parse_end, ==, 0);
	g_assert_cmpuint(extension->serialize, ==, 1);

	g_assert_finalize_object(extension);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_set_nonfatal_assertions();

	g_test_add_func("/xeme/extension/properties",
	                test_xeme_extension_properties);

	g_test_add_func("/xeme/extension/normal/parse",
	                test_xeme_extension_normal_parse_test);
	g_test_add_func("/xeme/extension/normal/serialize",
	                test_xeme_extension_normal_serialize_test);

	return g_test_run();
}

0707010000001D000081A400000000000000000000000167AEF51100001817000000000000000000000000000000000000002800000000xeme-0+53/xeme/tests/testextensionmap.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Add/Remove Tests
 *****************************************************************************/
static void
test_xeme_extension_map_add_remove(void) {
	XemeExtensionMap *map = NULL;
	XemeExtension *extension = NULL;
	XemeExtension *lookup = NULL;
	gboolean ret = FALSE;

	map = xeme_extension_map_new();
	g_assert_cmpuint(xeme_extension_map_get_size(map), ==, 0);

	extension = g_object_new(
		XEME_TYPE_EXTENSION,
		"element", "foobar",
		"namespace", "baz",
		NULL);

	/* Add our extension to the map. */
	ret = xeme_extension_map_add(map, extension);
	g_assert_true(ret);
	g_assert_cmpuint(xeme_extension_map_get_size(map), ==, 1);

	/* Look up the extension we added. */
	lookup = xeme_extension_map_lookup(map, "foobar", "baz");
	g_assert_true(XEME_IS_EXTENSION(lookup));
	g_assert_true(lookup == extension);

	/* Look up a fake extension. */
	lookup = xeme_extension_map_lookup(map, "pidgy", "pie");
	g_assert_null(lookup);

	/* Remove our extension. */
	ret = xeme_extension_map_remove(map, extension);
	g_assert_true(ret);
	g_assert_cmpuint(xeme_extension_map_get_size(map), ==, 0);

	/* Make sure our extension is gone. */
	lookup = xeme_extension_map_lookup(map, "foobar", "baz");
	g_assert_null(lookup);

	/* Cleanup. */
	g_clear_object(&extension);
	g_clear_object(&map);
}

/******************************************************************************
 * Parse Tests
 *****************************************************************************/
G_DECLARE_FINAL_TYPE(TestXemeExtension, test_xeme_extension, TEST_XEME,
                     EXTENSION, XemeExtension)

struct _TestXemeExtension {
	XemeExtension parent;

	guint parse_counter;
};

static void
test_xeme_extension_parse_start(XemeExtension *extension,
                                XemeInputStream *input,
                                GMarkupParseContext *context)
{
	TestXemeExtension *test_extension = NULL;

	g_assert_true(TEST_XEME_IS_EXTENSION(extension));
	g_assert_true(XEME_IS_INPUT_STREAM(input));
	g_assert_nonnull(context);

	test_extension = TEST_XEME_EXTENSION(extension);

	test_extension->parse_counter++;
}

G_DEFINE_FINAL_TYPE(TestXemeExtension, test_xeme_extension,
                    XEME_TYPE_EXTENSION)

static void
test_xeme_extension_init(TestXemeExtension *extension) {
	extension->parse_counter = 0;
}

static void
test_xeme_extension_class_init(TestXemeExtensionClass *klass) {
	XemeExtensionClass *extension_class = XEME_EXTENSION_CLASS(klass);

	extension_class->parse_start = test_xeme_extension_parse_start;
}

static void
test_xeme_extension_map_parser_start_element(GMarkupParseContext *context,
                                             const char *element_name,
                                             G_GNUC_UNUSED const char **attribute_names,
                                             G_GNUC_UNUSED const char **attribute_values,
                                             gpointer user_data,
                                             G_GNUC_UNUSED GError **error)
{
	XemeExtensionMap *map = user_data;

	if(xeme_str_equal(element_name, "elements")) {
		XemeInputStream *stream = xeme_input_stream_new();
		xeme_extension_map_parse_start(map, stream, context);
		g_clear_object(&stream);
	}
}

static void
test_xeme_extension_map_parser_end_element(GMarkupParseContext *context,
                                           const char *element_name,
                                           gpointer user_data,
                                           G_GNUC_UNUSED GError **error)
{
	XemeExtensionMap *map = user_data;

	if(xeme_str_equal(element_name, "elements")) {
		xeme_extension_map_parse_end(map, context);
	}
}

static GMarkupParser test_xeme_extension_map_parser = {
	.start_element = test_xeme_extension_map_parser_start_element,
	.end_element = test_xeme_extension_map_parser_end_element
};

static void
test_xeme_extension_map_parse(void) {
	TestXemeExtension *extension = NULL;
	XemeExtensionMap *map = NULL;
	GError *error = NULL;
	GMarkupParseContext *context = NULL;
	const char *data = "<elements><foobar xmlns='baz'/><qux xmlns='quux'/></elements>";
	gboolean ret = FALSE;

	map = xeme_extension_map_new();

	extension = g_object_new(
		test_xeme_extension_get_type(),
		"element", "foobar",
		"namespace", "baz",
		NULL);
	xeme_extension_map_add(map, XEME_EXTENSION(extension));

	context = g_markup_parse_context_new(&test_xeme_extension_map_parser,
	                                     G_MARKUP_PREFIX_ERROR_POSITION,
	                                     map, NULL);
	ret = g_markup_parse_context_parse(context, data, strlen(data), &error);
	g_assert_no_error(error);
	g_assert_true(ret);

	g_assert_cmpuint(extension->parse_counter, ==, 1);

	g_assert_finalize_object(map);
	g_clear_pointer(&context, g_markup_parse_context_free);
	g_clear_object(&extension);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_set_nonfatal_assertions();

	g_test_add_func("/xeme/extension-map/add-remove",
	                test_xeme_extension_map_add_remove);

	g_test_add_func("/xeme/extension-map/parse",
	                test_xeme_extension_map_parse);

	return g_test_run();
}
0707010000001E000081A400000000000000000000000167AEF511000015C6000000000000000000000000000000000000002700000000xeme-0+53/xeme/tests/testinputstream.c/*
 * Copyright (C) 2023-2024 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Callbacks
 *****************************************************************************/
/**
 * test_xeme_input_stream_closed_cb: (skip)
 * @stream: The instance.
 * @data: a pointer to a guint to be incremented.
 *
 * Increments @data to verify how many times the [signal@InputStream::closed]
 * signal has been called.
 */
static void
xeme_input_stream_stream_closed_cb(G_GNUC_UNUSED XemeInputStream *stream,
                                   gpointer data)
{
	guint *counter = data;

	*counter = *counter + 1;
}

/**
 * test_xeme_connection_timeout_cb: (skip)
 * @data: The main loop to quit.
 *
 * Used for callbacks for timing out the main loops when running tests. If this
 * is called, it will assert that it was reached and cause the test to fail.
 *
 * Returns: G_SOURCE_REMOVE to not be called again.
 */
static gboolean
test_xeme_input_stream_timeout_cb(gpointer data) {
	g_main_loop_quit(data);

	g_assert_not_reached();

	return G_SOURCE_REMOVE;
}

static gboolean
test_xeme_input_stream_quit_cb(gpointer data) {
	g_main_loop_quit(data);

	return G_SOURCE_REMOVE;
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_input_stream_start(void) {
	XemeInputStream *input_stream = NULL;
	XemeOutputStream *output_stream = NULL;
	GError *error = NULL;
	GMainLoop *loop = NULL;
	GInputStream *input = NULL;
	gboolean ret = FALSE;
	const char *input_data = "" \
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
		"<stream:stream from=\"juliet@im.example.com\" "
		"to=\"im.example.com\" id=\"t7AMCin9zjMNwQKDnplntZPIDEI=\" "
		"version=\"1.0\" xml:lang=\"en\" xmlns=\"jabber:client\" "
		"xmlns:stream=\"http://etherx.jabber.org/streams\">"
		"</stream:stream>";
	const char *value = NULL;
	guint counter = 0;

	output_stream = xeme_output_stream_new("julient@im.example.com",
	                                       "im.example.com", NULL);

	input_stream = xeme_input_stream_new();
	g_signal_connect(input_stream, "closed",
	                 G_CALLBACK(xeme_input_stream_stream_closed_cb), &counter);

	input = g_memory_input_stream_new_from_data(input_data, -1, NULL);

	ret = xeme_input_stream_start(input_stream, input, output_stream, &error);
	g_clear_object(&output_stream);

	g_assert_no_error(error);
	g_assert_true(ret);

	/* Create our main loop and add some timeouts and stuff. */
	loop = g_main_loop_new(NULL, FALSE);
	g_timeout_add(2, test_xeme_input_stream_quit_cb, loop);
	g_timeout_add(100, test_xeme_input_stream_timeout_cb, loop);

	g_main_loop_run(loop);

	/* If we made it here, the quit cb got called, so verify everything. */
	value = xeme_stream_get_from(XEME_STREAM(input_stream));
	g_assert_cmpstr(value, ==, "juliet@im.example.com");

	value = xeme_stream_get_to(XEME_STREAM(input_stream));
	g_assert_cmpstr(value, ==, "im.example.com");

	value = xeme_stream_get_id(XEME_STREAM(input_stream));
	g_assert_cmpstr(value, ==, "t7AMCin9zjMNwQKDnplntZPIDEI=");

	value = xeme_stream_get_version(XEME_STREAM(input_stream));
	g_assert_cmpstr(value, ==, "1.0");

	value = xeme_stream_get_language(XEME_STREAM(input_stream));
	g_assert_cmpstr(value, ==, "en");

	/* Make sure the close signal was emitted. */
	g_assert_cmpuint(counter, ==, 1);

	g_clear_object(&input);
	g_clear_object(&input_stream);
}

/******************************************************************************
 * Restart Requested Test
 *****************************************************************************/
static void
test_xeme_input_stream_restart_requested_cb(XemeInputStream *stream,
                                            gpointer data)
{
	guint *counter = data;

	g_assert_true(XEME_IS_INPUT_STREAM(stream));

	*counter = *counter + 1;
}

static void
test_xeme_input_stream_restart_requested(void) {
	XemeInputStream *stream = NULL;
	guint counter = 0;

	stream = xeme_input_stream_new();
	g_signal_connect(stream, "restart-requested",
	                 G_CALLBACK(test_xeme_input_stream_restart_requested_cb),
	                 &counter);

	g_assert_cmpuint(counter, ==, 0);
	xeme_input_stream_restart_requested(stream);
	g_assert_cmpuint(counter, ==, 1);

	g_assert_finalize_object(stream);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/xeme/input-stream/start",
	                test_xeme_input_stream_start);
	g_test_add_func("/xeme/input-stream/restart-requested",
	                test_xeme_input_stream_restart_requested);

	return g_test_run();
}

0707010000001F000081A400000000000000000000000167AEF5110000244A000000000000000000000000000000000000002300000000xeme-0+53/xeme/tests/testmessage.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_message_new(void) {
	XemeStanza *message = NULL;

	message = xeme_message_new();
	g_assert_true(XEME_IS_MESSAGE(message));

	g_clear_object(&message);
}

static void
test_xeme_message_properties(void) {
	XemeMessage *message = NULL;
	char *thread = NULL;
	char *thread_parent = NULL;

	message = g_object_new(
		XEME_TYPE_MESSAGE,
		"thread", "thread1",
		"thread-parent", "thread-parent1",
		NULL);

	g_assert_true(XEME_IS_MESSAGE(message));

	g_object_get(
		message,
		"thread", &thread,
		"thread-parent", &thread_parent,
		NULL);

	g_assert_cmpstr(thread, ==, "thread1");
	g_clear_pointer(&thread, g_free);

	g_assert_cmpstr(thread_parent, ==, "thread-parent1");
	g_clear_pointer(&thread_parent, g_free);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_empty(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;

	message = xeme_message_new();
	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, "<message></message>");
	g_clear_pointer(&data, g_free);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_all_attributes(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;
	const char *expected = "<message from=\"sender1\" id=\"unit-test-1\" "\
	                       "to=\"recipient1\" type=\"unit-test\" " \
	                       "xml:lang=\"en_TESTING\"></message>";

	message = xeme_message_new();
	xeme_stanza_set_content_type(message, "unit-test");
	xeme_stanza_set_from(message, "sender1");
	xeme_stanza_set_id(message, "unit-test-1");
	xeme_stanza_set_to(message, "recipient1");
	xeme_stanza_set_xml_lang(message, "en_TESTING");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_clear_pointer(&data, g_free);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_subject_default(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;

	message = xeme_message_new();

	xeme_message_add_subject(XEME_MESSAGE(message), NULL, "xeme");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==,
	                "<message><subject>xeme</subject></message>");
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_subject_duplicate(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;
	const char *expected = "<message><subject>two</subject></message>";

	message = xeme_message_new();
	xeme_message_add_subject(XEME_MESSAGE(message), NULL, "one");
	xeme_message_add_subject(XEME_MESSAGE(message), NULL, "two");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_subject_multiple(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;
	const char *expected = "" \
		"<message><subject>Greetings!</subject>" \
		"<subject xml:lang=\"es\">¡Saludos!</subject>" \
		"<subject xml:lang=\"fr\">Salutations!</subject></message>";

	message = xeme_message_new();
	xeme_message_add_subject(XEME_MESSAGE(message), NULL, "Greetings!");
	xeme_message_add_subject(XEME_MESSAGE(message), "es", "¡Saludos!");
	xeme_message_add_subject(XEME_MESSAGE(message), "fr", "Salutations!");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_body_default(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;

	message = xeme_message_new();

	xeme_message_add_body(XEME_MESSAGE(message), NULL, "this is the body");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==,
	                "<message><body>this is the body</body></message>");
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_body_duplicate(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;
	const char *expected = "<message><body>two</body></message>";

	message = xeme_message_new();
	xeme_message_add_body(XEME_MESSAGE(message), NULL, "one");
	xeme_message_add_body(XEME_MESSAGE(message), NULL, "two");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_body_multiple(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data = NULL;
	const char *expected = "" \
		"<message><body>Hello World!</body>" \
		"<body xml:lang=\"es\">¡Hola Mundo!</body>" \
		"<body xml:lang=\"fr\">Bonjour le monde!</body></message>";

	message = xeme_message_new();
	xeme_message_add_body(XEME_MESSAGE(message), NULL, "Hello World!");
	xeme_message_add_body(XEME_MESSAGE(message), "es", "¡Hola Mundo!");
	xeme_message_add_body(XEME_MESSAGE(message), "fr", "Bonjour le monde!");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_thread(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data;
	const char *expected = "<message><thread>thread1</thread></message>";

	message = xeme_message_new();
	xeme_message_set_thread(XEME_MESSAGE(message), "thread1");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_thread_with_parent(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data;
	const char *expected = "" \
		"<message><thread parent=\"parent1\">thread1</thread></message>";

	message = xeme_message_new();
	xeme_message_set_thread(XEME_MESSAGE(message), "thread1");
	xeme_message_set_thread_parent(XEME_MESSAGE(message), "parent1");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

static void
test_xeme_message_marshal_thread_parent_only(void) {
	XemeStanza *message = NULL;
	GError *error = NULL;
	char *data;
	const char *expected = "<message></message>";

	message = xeme_message_new();
	xeme_message_set_thread_parent(XEME_MESSAGE(message), "parent1");

	data = xeme_stanza_marshal(message, &error);

	g_assert_no_error(error);

	g_assert_cmpstr(data, ==, expected);
	g_free(data);

	g_clear_object(&message);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/xeme/message/new", test_xeme_message_new);
	g_test_add_func("/xeme/message/properties", test_xeme_message_properties);

	g_test_add_func("/xeme/message/marshal/empty",
	                test_xeme_message_marshal_empty);
	g_test_add_func("/xeme/message/marshal/all-attributes",
	                test_xeme_message_marshal_all_attributes);
	g_test_add_func("/xeme/message/marshal/subject/default",
	                test_xeme_message_marshal_subject_default);
	g_test_add_func("/xeme/message/marshal/subject/duplicate",
	                test_xeme_message_marshal_subject_duplicate);
	g_test_add_func("/xeme/message/marshal/subject/multiple",
	                test_xeme_message_marshal_subject_multiple);
	g_test_add_func("/xeme/message/marshal/body/default",
	                test_xeme_message_marshal_body_default);
	g_test_add_func("/xeme/message/marshal/body/duplicate",
	                test_xeme_message_marshal_body_duplicate);
	g_test_add_func("/xeme/message/marshal/body/multiple",
	                test_xeme_message_marshal_body_multiple);
	g_test_add_func("/xeme/message/marshal/thread",
	                test_xeme_message_marshal_thread);
	g_test_add_func("/xeme/message/marshal/thread-with-parent",
	                test_xeme_message_marshal_thread_with_parent);
	g_test_add_func("/xeme/message/marshal/thread-parent-only",
	                test_xeme_message_marshal_thread_parent_only);

	return g_test_run();
}
07070100000020000081A400000000000000000000000167AEF51100001737000000000000000000000000000000000000002800000000xeme-0+53/xeme/tests/testoutputstream.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Callbacks
 *****************************************************************************/

/**
 * test_xeme_connection_timeout_cb: (skip)
 * data: The main loop to quit.
 *
 * Used for callbacks for timing out the main loops when running tests. If this
 * is called, it will assert that it was reached and cause the test to fail.
 *
 * Returns: G_SOURCE_REMOVE to not be called again.
 */
static gboolean
test_xeme_output_stream_timeout_cb(gpointer data) {
	g_main_loop_quit(data);

	g_assert_not_reached();

	return G_SOURCE_REMOVE;
}

static gboolean
test_xeme_output_stream_quit_cb(gpointer data) {
	g_main_loop_quit(data);

	return G_SOURCE_REMOVE;
}

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_output_stream_start(void) {
	XemeOutputStream *stream = NULL;
	GError *error = NULL;
	GMainLoop *loop = NULL;
	GOutputStream *output = NULL;
	gboolean ret = FALSE;
	const char *actual = NULL;
	const char *expected = "" \
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
		"<stream:stream from=\"juliet@im.example.com\" "
		"to=\"im.example.com\" id=\"t7AMCin9zjMNwQKDnplntZPIDEI=\" "
		"version=\"1.0\" xml:lang=\"en\" xmlns=\"jabber:client\" "
		"xmlns:stream=\"http://etherx.jabber.org/streams\">";

	stream = xeme_output_stream_new("im.example.com", "juliet@im.example.com",
	                                NULL);
	xeme_stream_set_id(XEME_STREAM(stream), "t7AMCin9zjMNwQKDnplntZPIDEI=");
	xeme_stream_set_language(XEME_STREAM(stream), "en");

	output = g_memory_output_stream_new_resizable();

	ret = xeme_output_stream_start(stream, output, &error);

	g_assert_no_error(error);
	g_assert_true(ret);

	/* Create our main loop and add some timeouts and stuff. */
	loop = g_main_loop_new(NULL, FALSE);
	g_timeout_add(2, test_xeme_output_stream_quit_cb, loop);
	g_timeout_add(100, test_xeme_output_stream_timeout_cb, loop);

	g_main_loop_run(loop);

	/* If we made it here, the quit cb got called, so verify the output. */
	g_output_stream_flush(output, NULL, &error);
	g_assert_no_error(error);

	actual = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(output));
	g_assert_cmpstr(actual, ==, expected);

	g_clear_object(&output);
	g_clear_object(&stream);
}

static void
test_xeme_output_stream_restart_not_running(void) {
	XemeOutputStream *stream = NULL;
	GError *error = NULL;
	gboolean result = FALSE;

	stream = xeme_output_stream_new("foo", "bar", NULL);

	result = xeme_output_stream_restart(stream, &error);
	g_assert_error(error, XEME_DOMAIN, 0);
	g_clear_error(&error);
	g_assert_false(result);

	g_clear_object(&stream);
}

static void
test_xeme_output_stream_restart_running(void) {
	XemeOutputStream *stream = NULL;
	GError *error = NULL;
	GMainLoop *loop = NULL;
	GOutputStream *output = NULL;
	gboolean ret = FALSE;
	const char *actual = NULL;
	const char *expected = "" \
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
		"<stream:stream from=\"juliet@im.example.com\" "
		"to=\"im.example.com\" id=\"t7AMCin9zjMNwQKDnplntZPIDEI=\" "
		"version=\"1.0\" xml:lang=\"en\" xmlns=\"jabber:client\" "
		"xmlns:stream=\"http://etherx.jabber.org/streams\">"
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
		"<stream:stream from=\"juliet@im.example.com\" "
		"to=\"im.example.com\" id=\"t7AMCin9zjMNwQKDnplntZPIDEI=\" "
		"version=\"1.0\" xml:lang=\"en\" xmlns=\"jabber:client\" "
		"xmlns:stream=\"http://etherx.jabber.org/streams\">";

	stream = xeme_output_stream_new("im.example.com", "juliet@im.example.com",
	                                NULL);
	xeme_stream_set_id(XEME_STREAM(stream), "t7AMCin9zjMNwQKDnplntZPIDEI=");
	xeme_stream_set_language(XEME_STREAM(stream), "en");

	output = g_memory_output_stream_new_resizable();

	ret = xeme_output_stream_start(stream, output, &error);
	g_assert_no_error(error);
	g_assert_true(ret);

	ret = xeme_output_stream_restart(stream, &error);
	g_assert_no_error(error);
	g_assert_true(ret);

	/* Create our main loop and add some timeouts and stuff. */
	loop = g_main_loop_new(NULL, FALSE);
	g_timeout_add(2, test_xeme_output_stream_quit_cb, loop);
	g_timeout_add(100, test_xeme_output_stream_timeout_cb, loop);

	g_main_loop_run(loop);

	/* If we made it here we can verify the output. */
	actual = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(output));
	g_assert_cmpstr(actual, ==, expected);

	g_clear_object(&output);
	g_clear_object(&stream);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_set_nonfatal_assertions();

	g_test_add_func("/xeme/output-stream/start",
	                test_xeme_output_stream_start);

	g_test_add_func("/xeme/output-stream/restart/not-running",
	                test_xeme_output_stream_restart_not_running);
	g_test_add_func("/xeme/output-stream/restart/running",
	                test_xeme_output_stream_restart_running);

	return g_test_run();
}

07070100000021000081A400000000000000000000000167AEF51100000888000000000000000000000000000000000000002200000000xeme-0+53/xeme/tests/teststanza.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_stanza_properties(void) {
	XemeStanza *stanza = NULL;
	char *content_type = NULL;
	char *from = NULL;
	char *id = NULL;
	char *to = NULL;
	char *xml_lang = NULL;

	stanza = g_object_new(
		XEME_TYPE_STANZA,
		"content-type", "content-type1",
		"from", "sender1",
		"id", "id1",
		"to", "recipient1",
		"xml-lang", "xml_lang1",
		NULL);

	g_object_get(
		G_OBJECT(stanza),
		"content-type", &content_type,
		"from", &from,
		"id", &id,
		"to", &to,
		"xml-lang", &xml_lang,
		NULL);

	g_assert_cmpstr(content_type, ==, "content-type1");
	g_clear_pointer(&content_type, g_free);

	g_assert_cmpstr(from, ==, "sender1");
	g_clear_pointer(&from, g_free);

	g_assert_cmpstr(id, ==, "id1");
	g_clear_pointer(&id, g_free);

	g_assert_cmpstr(to, ==, "recipient1");
	g_clear_pointer(&to, g_free);

	g_assert_cmpstr(xml_lang, ==, "xml_lang1");
	g_clear_pointer(&xml_lang, g_free);

	g_clear_object(&stanza);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/xeme/stanza/properties", test_xeme_stanza_properties);

	return g_test_run();
}
07070100000022000081A400000000000000000000000167AEF51100000B2F000000000000000000000000000000000000002200000000xeme-0+53/xeme/tests/teststream.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <glib.h>

#include <xeme.h>

/******************************************************************************
 * Tests
 *****************************************************************************/
static void
test_xeme_stream_properties(void) {
	XemeStream *stream = NULL;
	GCancellable *cancellable = NULL;
	GCancellable *cancellable1 = NULL;
	char *id = NULL;
	char *to = NULL;
	char *from = NULL;
	char *language = NULL;
	char *version = NULL;

	cancellable = g_cancellable_new();
	stream = g_object_new(
		XEME_TYPE_STREAM,
		"id", "id1",
		"to", "to1",
		"from", "from1",
		"language", "language1",
		"cancellable", cancellable,
		"version", "version1",
		NULL);

	g_assert_true(XEME_IS_STREAM(stream));

	g_object_get(
		G_OBJECT(stream),
		"id", &id,
		"to", &to,
		"from", &from,
		"language", &language,
		"cancellable", &cancellable1,
		"version", &version,
		NULL);

	g_assert_cmpstr(id, ==, "id1");
	g_clear_pointer(&id, g_free);

	g_assert_cmpstr(to, ==, "to1");
	g_clear_pointer(&to, g_free);

	g_assert_cmpstr(from, ==, "from1");
	g_clear_pointer(&from, g_free);

	g_assert_cmpstr(language, ==, "language1");
	g_clear_pointer(&language, g_free);

	g_assert_true(G_IS_CANCELLABLE(cancellable1));
	g_assert_true(cancellable == cancellable1);
	g_clear_object(&cancellable1);

	g_assert_cmpstr(version, ==, "version1");
	g_clear_pointer(&version, g_free);

	g_clear_object(&cancellable);
	g_clear_object(&stream);
}

static void
test_xeme_stream_default_version(void) {
	XemeStream *stream = g_object_new(XEME_TYPE_STREAM, NULL);
	const char *version = NULL;

	version = xeme_stream_get_version(stream);
	g_assert_cmpstr(version, ==, XEME_STREAM_VERSION);

	g_clear_object(&stream);
}

/******************************************************************************
 * Main
 *****************************************************************************/
int
main(int argc, char *argv[]) {
	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/xeme/stream/properties", test_xeme_stream_properties);
	g_test_add_func("/xeme/stream/default-version",
	                test_xeme_stream_default_version);

	return g_test_run();
}
07070100000023000081A400000000000000000000000167AEF51100000471000000000000000000000000000000000000001900000000xeme-0+53/xeme/xeme.h.in/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_H
#define XEME_H

#ifndef __GI_SCANNER__ /* hide this bit from g-ir-scanner */
# ifdef XEME_COMPILATION
#  error "xeme source files should not be including xeme.h"
# endif /* XEME_COMPILATION */
#endif /* __GI_SCANNER__ */

#ifndef XEME_GLOBAL_HEADER_INSIDE
# define XEME_GLOBAL_HEADER_INSIDE
#endif /* XEME_GLOBAL_HEADER_INSIDE */

@XEME_H_INCLUDES@

#undef XEME_GLOBAL_HEADER_INSIDE

#endif /* XEME_H */
07070100000024000081A400000000000000000000000167AEF51100000A49000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xemeaddress.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeaddress.h"

#include "xemestring.h"

#define XEME_ADDRESS_REGEX \
	"^(?P<bare_jid>" \
		"(?:(?P<local>[^\\\"&'/:<>@]+)@)?" \
		"(?P<domain>(?:[^@/[\\\\_$]+|(\\[[0-9a-fA-F:]+\\])))" \
	")" \
	"(?:/(?P<resource>.+))?$"

/******************************************************************************
 * Public API
 *****************************************************************************/
gboolean
xeme_address_parse(const char *address, char **bare_jid, char **local,
                   char **domain, char **resource)
{
	static GRegex *regexp = NULL;
	GMatchInfo *info = NULL;
	gchar *tmp = NULL;

	g_return_val_if_fail(!xeme_str_is_empty(address), FALSE);

	if(regexp == NULL) {
		GError *error = NULL;

		regexp = g_regex_new(XEME_ADDRESS_REGEX, G_REGEX_DEFAULT,
		                     G_REGEX_MATCH_DEFAULT, &error);

		if(error != NULL) {
			g_critical("failed to compiled the regular expression for "
			           "addresses %s",
			           error->message != NULL ? error->message : "unknown");

			g_clear_error(&error);
		}
	}

	if(!g_regex_match(regexp, address, G_REGEX_MATCH_DEFAULT, &info)) {
		g_clear_pointer(&info, g_match_info_free);

		return FALSE;
	}

	if(bare_jid != NULL) {
		tmp = g_match_info_fetch_named(info, "bare_jid");
		if(xeme_str_is_empty(tmp)) {
			g_clear_pointer(&tmp, g_free);
		}

		*bare_jid = tmp;
	}

	if(local != NULL) {
		tmp = g_match_info_fetch_named(info, "local");
		if(xeme_str_is_empty(tmp)) {
			g_clear_pointer(&tmp, g_free);
		}

		*local = tmp;
	}

	if(domain != NULL) {
		tmp = g_match_info_fetch_named(info, "domain");
		if(xeme_str_is_empty(tmp)) {
			g_clear_pointer(&tmp, g_free);
		}

		*domain = tmp;
	}

	if(resource != NULL) {
		tmp = g_match_info_fetch_named(info, "resource");
		if(xeme_str_is_empty(tmp)) {
			g_clear_pointer(&tmp, g_free);
		}

		*resource = tmp;
	}

	g_clear_pointer(&info, g_match_info_free);

	return TRUE;
}
07070100000025000081A400000000000000000000000167AEF511000006C0000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xemeaddress.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_ADDRESS_H
#define XEME_ADDRESS_H

#include <glib.h>
#include <glib-object.h>

#include <xeme/xemeversion.h>

G_BEGIN_DECLS

/**
 * xeme_address_parse:
 * @address: The address to parse.
 * @bare_jid: (nullable): An optional return address for the bare jid, that is
 *            the `local@domain` part of the jid including the `@`.
 * @local: (nullable): An optional return address for the localpart.
 * @domain: (nullable): An optional return address for the domainpart.
 * @resource: (nullable): An optional return address for the resourcepart.
 *
 * Splits @address according to the rules of
 * [RFC 6122](https://www.rfc-editor.org/rfc/rfc6122#section-2) and returns the
 * results into the optional @local, @domain, and @resource.
 *
 * Returns: %TRUE if @address was valid, otherwise %FALSE.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_address_parse(const char *address, char **bare_jid, char **local, char **domain, char **resource);

G_END_DECLS

#endif /* XEME_ADDRESS_H */
07070100000026000081A400000000000000000000000167AEF51100003D55000000000000000000000000000000000000002000000000xeme-0+53/xeme/xemeconnection.c/*
 * Copyright (C) 2023-2024 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeconnection.h"

#include "xemeaddress.h"
#include "xemeconstants.h"
#include "xemeenums.h"
#include "xemestring.h"

enum {
	PROP_0,
	PROP_JID,
	PROP_RESOURCE,
	PROP_SERVER,
	PROP_PORT,
	PROP_TLS_MODE,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

struct _XemeConnection {
	GObject parent;

	char *jid;
	char *domain; /* This is parsed out of the jid when it is set. */
	char *resource;

	char *server;
	guint16 port;

	XemeTlsMode tls_mode;

	GSocketClient *client;
	GSocketConnection *socket_connection;

	XemeInputStream *input;

	GCancellable *cancellable;
};

G_DEFINE_FINAL_TYPE(XemeConnection, xeme_connection, G_TYPE_OBJECT)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
xeme_connection_set_jid(XemeConnection *connection, const char *jid) {
	char *bare_jid = NULL;
	char *domain = NULL;
	char *resource = NULL;

	if(!xeme_address_parse(jid, &bare_jid, NULL, &domain, &resource)) {
		g_critical("invalid jid '%s'", jid);

		return;
	}

	g_free(connection->jid);
	connection->jid = bare_jid;
	connection->domain = domain;

	if(resource != NULL) {
		xeme_connection_set_resource(connection, resource);
		g_free(resource);
	}
}

static void
xeme_connection_connect_success(XemeConnection *connection, GTask *task) {
	XemeOutputStream *output = NULL;
	GError *error = NULL;
	GIOStream *iostream = NULL;
	GInputStream *istream = NULL;
	GOutputStream *ostream = NULL;

	iostream = G_IO_STREAM(connection->socket_connection);

	ostream = g_io_stream_get_output_stream(iostream);
	output = xeme_output_stream_new(connection->domain, connection->jid,
	                                connection->cancellable);
	if(!xeme_output_stream_start(output, ostream, &error)) {
		g_task_return_error(task, error);

		g_clear_object(&task);
		g_clear_object(&output);

		xeme_connection_close(connection, NULL);

		return;
	}

	istream = g_io_stream_get_input_stream(iostream);
	if(!xeme_input_stream_start(connection->input, istream, output, &error)) {
		g_task_return_error(task, error);

		g_clear_object(&task);
		g_clear_object(&output);

		xeme_connection_close(connection, NULL);

		return;
	}

	/* Finally return the output stream. */
	g_task_return_pointer(task, output, g_object_unref);

	g_clear_object(&task);
}

/******************************************************************************
 * connect_to_host functions
 *****************************************************************************/
static void
xeme_connection_connect_to_host_cb(GObject *source, GAsyncResult *result,
                                   gpointer data)
{
	XemeConnection *connection = NULL;
	GError *error = NULL;
	GSocketClient *client = G_SOCKET_CLIENT(source);
	GSocketConnection *socket_connection = NULL;
	GTask *task = data;

	connection = g_task_get_source_object(task);
	socket_connection = g_socket_client_connect_to_host_finish(client, result,
	                                                           &error);
	if(error != NULL) {
		g_task_return_error(task, error);
		g_clear_object(&task);

		return;
	}

	connection->socket_connection = socket_connection;

	xeme_connection_connect_success(connection, task);
}

static void
xeme_connection_connect_to_host(XemeConnection *connection, GTask *task) {
	gboolean use_tls = FALSE;
	const char *server = NULL;

	g_return_if_fail(XEME_IS_CONNECTION(connection));
	g_return_if_fail(G_IS_TASK(task));

	use_tls = (connection->tls_mode == XEME_TLS_MODE_DIRECT_TLS);
	g_socket_client_set_tls(connection->client, use_tls);

	server = connection->server;
	if(xeme_str_is_empty(server)) {
		server = connection->domain;
	}

	g_socket_client_connect_to_host_async(connection->client,
	                                      server,
	                                      connection->port,
	                                      connection->cancellable,
	                                      xeme_connection_connect_to_host_cb,
	                                      task);
}

/******************************************************************************
 * connect_to_service functions
 *****************************************************************************/
static void
xeme_connection_connect_to_service_cb(GObject *source, GAsyncResult *result,
                                      gpointer data)
{
	XemeConnection *connection = NULL;
	GError *error = NULL;
	GSocketClient *client = G_SOCKET_CLIENT(source);
	GSocketConnection *socket_connection = NULL;
	GTask *task = data;

	connection = g_task_get_source_object(task);
	socket_connection = g_socket_client_connect_to_service_finish(client,
	                                                              result,
	                                                              &error);
	if(error != NULL) {
		g_warning("failed to connect via srv record, attempting direct "
		          "connect.");

		xeme_connection_connect_to_host(connection, task);

		return;
	}

	connection->socket_connection = socket_connection;

	xeme_connection_connect_success(connection, task);
}

static void
xeme_connection_connect_to_service(XemeConnection *connection, GTask *task) {
	gboolean use_tls = TRUE;
	const char *service = XEME_SERVICE_XMPPS_CLIENT;

	g_return_if_fail(XEME_IS_CONNECTION(connection));
	g_return_if_fail(G_IS_TASK(task));

	if(connection->tls_mode != XEME_TLS_MODE_DIRECT_TLS) {
		service = XEME_SERVICE_XMPP_CLIENT;
		use_tls = FALSE;
	}

	g_socket_client_set_tls(connection->client, use_tls);

	g_socket_client_connect_to_service_async(connection->client,
	                                         connection->domain,
	                                         service,
	                                         connection->cancellable,
	                                         xeme_connection_connect_to_service_cb,
	                                         task);
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_connection_finalize(GObject *obj) {
	XemeConnection *connection = XEME_CONNECTION(obj);

	xeme_connection_close(connection, NULL);

	g_clear_pointer(&connection->jid, g_free);
	g_clear_pointer(&connection->domain, g_free);
	g_clear_pointer(&connection->resource, g_free);
	g_clear_pointer(&connection->server, g_free);

	g_clear_object(&connection->client);
	g_clear_object(&connection->socket_connection);

	g_clear_object(&connection->input);

	g_clear_object(&connection->cancellable);

	G_OBJECT_CLASS(xeme_connection_parent_class)->finalize(obj);
}

static void
xeme_connection_get_property(GObject *obj, guint param_id, GValue *value,
                             GParamSpec *pspec)
{
	XemeConnection *connection = XEME_CONNECTION(obj);

	switch(param_id) {
	case PROP_JID:
		g_value_set_string(value, xeme_connection_get_jid(connection));
		break;
	case PROP_RESOURCE:
		g_value_set_string(value, xeme_connection_get_resource(connection));
		break;
	case PROP_SERVER:
		g_value_set_string(value, xeme_connection_get_server(connection));
		break;
	case PROP_PORT:
		g_value_set_uint(value, xeme_connection_get_port(connection));
		break;
	case PROP_TLS_MODE:
		g_value_set_enum(value, xeme_connection_get_tls_mode(connection));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_connection_set_property(GObject *obj, guint param_id, const GValue *value,
                             GParamSpec *pspec)
{
	XemeConnection *connection = XEME_CONNECTION(obj);

	switch(param_id) {
	case PROP_JID:
		xeme_connection_set_jid(connection, g_value_get_string(value));
		break;
	case PROP_RESOURCE:
		xeme_connection_set_resource(connection, g_value_get_string(value));
		break;
	case PROP_SERVER:
		xeme_connection_set_server(connection, g_value_get_string(value));
		break;
	case PROP_PORT:
		xeme_connection_set_port(connection, g_value_get_uint(value));
		break;
	case PROP_TLS_MODE:
		xeme_connection_set_tls_mode(connection, g_value_get_enum(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_connection_init(G_GNUC_UNUSED XemeConnection *connection) {
}

static void
xeme_connection_class_init(XemeConnectionClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_connection_finalize;
	obj_class->get_property = xeme_connection_get_property;
	obj_class->set_property = xeme_connection_set_property;

	/**
	 * XemeConnection:jid:
	 *
	 * The "Jabber ID" which this connection originates from.
	 *
	 * Since: 0.1
	 */
	properties[PROP_JID] = g_param_spec_string(
		"jid", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeConnection:resource:
	 *
	 * The "resource" for the originator of this connection.
	 *
	 * If the `jid` passed to [ctor@Connection.new] includes a resource, it
	 * will be parsed out and saved here.
	 *
	 * If this is %NULL during connection, @connection will request one from
	 * the server and store it here. The server provided resource should be
	 * saved by the application and set by the application in the future.
	 *
	 * Resources are mostly deprecated, but this property can be modify to set
	 * it to whatever you like.
	 *
	 * Since: 0.1
	 */
	properties[PROP_RESOURCE] = g_param_spec_string(
		"resource", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeConnection:server:
	 *
	 * An explicit server to connect to.
	 *
	 * If this is set, it will be the only connection attempt made. See
	 * [method@Connection.connect_async] for more information.
	 *
	 * Since: 0.1
	 */
	properties[PROP_SERVER] = g_param_spec_string(
		"server", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeConnection:port:
	 *
	 * The explicit port to use.
	 *
	 * This will only be used if [property@Connection:server] is not %NULL.
	 *
	 * Since: 0.1
	 */
	properties[PROP_PORT] = g_param_spec_uint(
		"port", NULL, NULL,
		0, G_MAXUINT16, XEME_PORT_TLS,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeConnection:tls-mode:
	 *
	 * The TLS mode to use for this connection.
	 *
	 * This determines whether or not a TLS connection is attempted at all or
	 * if STARTTLS is used to upgrade a clear connection.
	 *
	 * Since: 0.1
	 */
	properties[PROP_TLS_MODE] = g_param_spec_enum(
		"tls-mode", NULL, NULL,
		XEME_TYPE_TLS_MODE,
		XEME_TLS_MODE_DIRECT_TLS,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
XemeConnection *
xeme_connection_new(const char *jid) {
	g_return_val_if_fail(!xeme_str_is_empty(jid), NULL);

	return g_object_new(
		XEME_TYPE_CONNECTION,
		"jid", jid,
		NULL);
}

const char *
xeme_connection_get_jid(XemeConnection *connection) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), NULL);

	return connection->jid;
}

const char *
xeme_connection_get_resource(XemeConnection *connection) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), NULL);

	return connection->resource;
}

void
xeme_connection_set_resource(XemeConnection *connection, const char *resource)
{
	g_return_if_fail(XEME_IS_CONNECTION(connection));

	if(g_set_str(&connection->resource, resource)) {
		g_object_notify_by_pspec(G_OBJECT(connection),
		                         properties[PROP_RESOURCE]);
	}
}

const char *
xeme_connection_get_server(XemeConnection *connection) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), NULL);

	return connection->server;
}

void
xeme_connection_set_server(XemeConnection *connection, const char *server) {
	g_return_if_fail(XEME_IS_CONNECTION(connection));

	if(g_set_str(&connection->server, server)) {
		g_object_notify_by_pspec(G_OBJECT(connection),
		                         properties[PROP_SERVER]);
	}
}

guint16
xeme_connection_get_port(XemeConnection *connection) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), 0);

	return connection->port;
}

void
xeme_connection_set_port(XemeConnection *connection, guint16 port) {
	g_return_if_fail(XEME_IS_CONNECTION(connection));

	if(connection->port != port) {
		connection->port = port;

		g_object_notify_by_pspec(G_OBJECT(connection), properties[PROP_PORT]);
	}
}

XemeTlsMode
xeme_connection_get_tls_mode(XemeConnection *connection) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), XEME_TLS_MODE_DIRECT_TLS);

	return connection->tls_mode;
}

void
xeme_connection_set_tls_mode(XemeConnection *connection, XemeTlsMode tls_mode)
{
	g_return_if_fail(XEME_IS_CONNECTION(connection));

	if(connection->tls_mode != tls_mode) {
		connection->tls_mode = tls_mode;

		g_object_notify_by_pspec(G_OBJECT(connection),
		                         properties[PROP_TLS_MODE]);
	}
}

void
xeme_connection_connect_async(XemeConnection *connection,
                              XemeInputStream *input,
                              GProxyResolver *resolver,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer data)
{
	GTask *task = NULL;

	g_return_if_fail(XEME_IS_CONNECTION(connection));
	g_return_if_fail(XEME_IS_INPUT_STREAM(input));

	if(G_IS_CANCELLABLE(cancellable)) {
		connection->cancellable = g_object_ref(cancellable);
	}

	connection->input = g_object_ref(input);

	connection->client = g_socket_client_new();
	if(G_IS_PROXY_RESOLVER(resolver)) {
		g_socket_client_set_proxy_resolver(connection->client, resolver);
		g_clear_object(&resolver);
	}

	task = g_task_new(connection, cancellable, callback, data);

	/* We're doing a direct connect to a server. */
	if(!xeme_str_is_empty(connection->server)) {
		xeme_connection_connect_to_host(connection, task);
	} else {
		xeme_connection_connect_to_service(connection, task);
	}
}

XemeOutputStream *
xeme_connection_connect_finish(XemeConnection *connection,
                               GAsyncResult *result,
                               GError **error)
{
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), FALSE);
	g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);

	return g_task_propagate_pointer(G_TASK(result), error);
}

gboolean
xeme_connection_close(XemeConnection *connection, GError **error) {
	g_return_val_if_fail(XEME_IS_CONNECTION(connection), FALSE);

	if(G_IS_IO_STREAM(connection->socket_connection)) {
		/* We're shutting down so we ignore errors. */
		g_io_stream_close(G_IO_STREAM(connection->socket_connection), NULL,
		                  error);
		g_clear_object(&connection->socket_connection);
	}

	g_clear_object(&connection->input);

	return TRUE;
}
07070100000027000081A400000000000000000000000167AEF5110000203A000000000000000000000000000000000000002000000000xeme-0+53/xeme/xemeconnection.h/*
 * Copyright (C) 2023-2024 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_CONNECTION_H
#define XEME_CONNECTION_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include "xemeinputstream.h"
#include "xemeoutputstream.h"
#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XemeConnection:
 *
 * A client to server connection.
 *
 * This object manages the connection to the server, handles feature
 * negotiation including STARTTLS, stream restarts, and other things that are
 * part of XMPP Core.
 *
 * Since: 0.1
 */

#define XEME_TYPE_CONNECTION (xeme_connection_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_FINAL_TYPE(XemeConnection, xeme_connection, XEME, CONNECTION,
                     GObject)

/**
 * XemeTlsMode:
 * @XEME_TLS_MODE_DIRECT_TLS: Use direct TLS.
 * @XEME_TLS_MODE_START_TLS: Use start TLS.
 * @XEME_TLS_MODE_NONE: Don't use TLS. This should really only be used in rare
 *                      circumstances like development.
 *
 * An enumeration representing which mode of TLS to use.
 *
 * Since: 0.1
 */
typedef enum {
	XEME_TLS_MODE_DIRECT_TLS,
	XEME_TLS_MODE_START_TLS,
	XEME_TLS_MODE_NONE,
} XemeTlsMode;

/**
 * xeme_connection_new:
 * @jid: (not nullable): The JID of the originator of this connection.
 *
 * Creates a new connection.
 *
 * If @jid includes a
 * [resource](https://www.rfc-editor.org/rfc/rfc6122#section-2), it will be
 * parsed out and stored in [property@Connection:resource].
 *
 * Returns: (transfer full): The new instance.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeConnection *xeme_connection_new(const char *jid);

/**
 * xeme_connection_get_jid:
 * @connection: The instance.
 *
 * Gets the bare [JID](https://www.rfc-editor.org/rfc/rfc6122#section-2) from
 * @connection.
 *
 * Returns: The bare jid.
 *
 * Since: 0.1
 */
const char *xeme_connection_get_jid(XemeConnection *connection);

/**
 * xeme_connection_get_resource:
 * @connection: The instance.
 *
 * Gets the resource from @connection.
 *
 * This may be %NULL in rare circumstances. However, @connection will request a
 * JID from the server during connection if this is %NULL.
 *
 * Returns: (transfer none) (nullable): The resource if set otherwise %NULL.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_connection_get_resource(XemeConnection *connection);

/**
 * xeme_connection_set_resource:
 * @connection: The instance.
 * @resource: (not nullable): The new resource.
 *
 * Sets the resource of @connection to @resource.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_connection_set_resource(XemeConnection *connection, const char *resource);

/**
 * xeme_connection_get_cancellable:
 * @connection: The instance.
 *
 * Gets the [class@Gio.Cancellable] for @connection.
 *
 * Returns: (transfer none) (nullable): The canncellable if one is set,
 *          otherwise %NULL.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
GCancellable *xeme_connection_get_cancellable(XemeConnection *connection);

/**
 * xeme_connection_get_server:
 * @connection: The instance.
 *
 * Gets the explicit server that @connection should connect to.
 *
 * See [method@Connection.connect_async] for more information.
 *
 * Returns: (nullable): The explicit server to connect to or %NULL.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_connection_get_server(XemeConnection *connection);

/**
 * xeme_connection_set_server:
 * @connection: The instance.
 * @server: (nullable): The new explicit server to connect to.
 *
 * Sets the explicit server to connect to.
 *
 * Passing %NULL for @server will unset any previous value.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_connection_set_server(XemeConnection *connection, const char *server);

/**
 * xeme_connection_get_port:
 * @connection: The instance.
 *
 * Gets the port from @connection.
 *
 * Returns: The port to use.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
guint16 xeme_connection_get_port(XemeConnection *connection);

/**
 * xeme_connection_set_port:
 * @connection: The instance.
 * @port: The new port value.
 *
 * Sets the port to use for @connection to @port.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_connection_set_port(XemeConnection *connection, guint16 port);

/**
 * xeme_connection_get_tls_mode:
 * @connection: The instance.
 *
 * Gets the [enum@TlsMode] from @connection.
 *
 * Returns: The TLS mode to use.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeTlsMode xeme_connection_get_tls_mode(XemeConnection *connection);

/**
 * xeme_connection_set_tls_mode:
 * @connection: The instance.
 * @tls_mode: The new TLS mode.
 *
 * Sets the [enum@TlsMode] of @connection to @tls_mode.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_connection_set_tls_mode(XemeConnection *connection, XemeTlsMode tls_mode);

/**
 * xeme_connection_connect_async:
 * @connection: The instance.
 * @input: The [class@InputStream] to use.
 * @resolver: (transfer full) (nullable): An optional proxy resolver.
 * @cancellable: (transfer none) (nullable): An optional cancellable.
 * @callback: (scope async) (nullable): A callback function.
 * @data: (nullable): User data pass to @callback.
 *
 * Starts the connection process.
 *
 * If [property@Connection:server] is not %NULL, then this will try to connect
 * directly to that server on the port specified by [property@Connection:port].
 *
 * If [property@Connection:server] is %NULL then DNS SRV records will be used
 * to connect per
 * [RFC 6120](https://www.rfc-editor.org/rfc/rfc6120#section-3.2.4).
 *
 * If [property@Connection:tls-mode] is set to `direct-tls`, then the
 * `_xmpps-client` service will be queried. If the DNS SRV lookup does not
 * return a record, an attempt will be made to connect directly to the domain
 * part of [property@Connection:jid] on the port specified by
 * [property@Connection:port].
 *
 * If [property@Connection:tls-mode] is set to `starttls` or `none` then the
 * `_xmpp-client` service will be queried. If the DNS SRV lookup does not
 * return a record, an attempt will be made to connect directly to the domain
 * part of [property@Connection:jid] on the port specified by
 * [property@Connection:port].
 *
 * If [property@Connection:tls-mode] is set to `starttls` then the connection
 * will attempt to upgrade to TLS during the connection process. If this fails,
 * the connection will be terminated.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_connection_connect_async(XemeConnection *connection, XemeInputStream *input, GProxyResolver *resolver, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data);

/**
 * xeme_connection_connect_finish:
 * @connection: The instance.
 * @result: The result passed to the callback.
 * @error: A return address for a #GError.
 *
 * Finishes a previous call to [method@Connection.connect_async].
 *
 * This should be call from the callback function of
 * [method@Connection.connect_async] to get the result of the connection
 * attempt.
 *
 * Returns: (transfer full): A new [class@OutputStream] or %NULL if the
 *          connection failed.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeOutputStream *xeme_connection_connect_finish(XemeConnection *connection, GAsyncResult *result, GError **error);

/**
 * xeme_connection_close:
 * @connection: The instance.
 * @error: (nullable): A return address for a #GError.
 *
 * Attempts to close the connection.
 *
 * Returns: %TRUE if sucessfull, otherwise %FALSE with @error possibly set.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_connection_close(XemeConnection *connection, GError **error);

G_END_DECLS

#endif /* XEME_CONNECTION_H */
07070100000028000081A400000000000000000000000167AEF51100000640000000000000000000000000000000000000001F00000000xeme-0+53/xeme/xemeconstants.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_CONSTANTS_H
#define XEME_CONSTANTS_H

#include <xeme/xemeversion.h>

/**
 * XEME_PORT_CLEAR:
 *
 * A constant representing the default clear text port.
 *
 * Since: 0.1
 */
#define XEME_PORT_CLEAR (5222) XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_PORT_TLS:
 *
 * A constant representing the default TLS port.
 *
 * Since: 0.1
 */
#define XEME_PORT_TLS (5223) XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_SERVICE_XMPP_CLIENT:
 *
 * A constant representing the xmpp-client service DNS record for clear text
 * connections.
 *
 * Since: 0.1
 */
#define XEME_SERVICE_XMPP_CLIENT ("xmpp-client") XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_SERVICE_XMPPS_CLIENT:
 *
 * A constant representing the xmpps-client service DNS record for TLS
 * connections.
 *
 * Since: 0.1
 */
#define XEME_SERVICE_XMPPS_CLIENT ("xmpps-client") XEME_AVAILABLE_MACRO_IN_0_1

#endif /* XEME_CONSTANTS_H */
07070100000029000081A400000000000000000000000167AEF5110000040A000000000000000000000000000000000000001A00000000xeme-0+53/xeme/xemecore.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_CORE_H
#define XEME_CORE_H

#include <glib.h>

#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XEME_DOMAIN:
 *
 * A constant to represent an error from Xeme.
 *
 * Since: 0.1
 */
#define XEME_DOMAIN (g_quark_from_static_string("xeme")) XEME_AVAILABLE_MACRO_IN_0_1

G_END_DECLS

#endif /* XEME_CORE_H */
0707010000002A000081A400000000000000000000000167AEF51100001884000000000000000000000000000000000000001F00000000xeme-0+53/xeme/xemeextension.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeextension.h"

#include "xemestring.h"

enum {
	PROP_0,
	PROP_ELEMENT,
	PROP_NAMESPACE,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

typedef struct {
	char *element;
	char *namespace;
} XemeExtensionPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(XemeExtension, xeme_extension, G_TYPE_OBJECT)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
xeme_extension_set_element(XemeExtension *extension, const char *element) {
	XemeExtensionPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_EXTENSION(extension));

	priv = xeme_extension_get_instance_private(extension);

	if(g_set_str(&priv->element, element)) {
		g_object_notify_by_pspec(G_OBJECT(extension),
		                         properties[PROP_ELEMENT]);
	}
}

static void
xeme_extension_set_namespace(XemeExtension *extension, const char *namespace) {
	XemeExtensionPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_EXTENSION(extension));

	priv = xeme_extension_get_instance_private(extension);

	if(g_set_str(&priv->namespace, namespace)) {
		g_object_notify_by_pspec(G_OBJECT(extension),
		                         properties[PROP_NAMESPACE]);
	}
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_extension_finalize(GObject *obj) {
	XemeExtension *extension = XEME_EXTENSION(obj);
	XemeExtensionPrivate *priv = NULL;

	priv = xeme_extension_get_instance_private(extension);

	g_clear_pointer(&priv->element, g_free);
	g_clear_pointer(&priv->namespace, g_free);

	G_OBJECT_CLASS(xeme_extension_parent_class)->finalize(obj);
}

static void
xeme_extension_get_property(GObject *obj, guint param_id, GValue *value,
                            GParamSpec *pspec)
{
	XemeExtension *extension = XEME_EXTENSION(obj);

	switch(param_id) {
	case PROP_ELEMENT:
		g_value_set_string(value, xeme_extension_get_element(extension));
		break;
	case PROP_NAMESPACE:
		g_value_set_string(value, xeme_extension_get_namespace(extension));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_extension_set_property(GObject *obj, guint param_id, const GValue *value,
                            GParamSpec *pspec)
{
	XemeExtension *extension = XEME_EXTENSION(obj);

	switch(param_id) {
	case PROP_ELEMENT:
		xeme_extension_set_element(extension, g_value_get_string(value));
		break;
	case PROP_NAMESPACE:
		xeme_extension_set_namespace(extension, g_value_get_string(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_extension_init(G_GNUC_UNUSED XemeExtension *extension) {
}

static void
xeme_extension_class_init(XemeExtensionClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_extension_finalize;
	obj_class->get_property = xeme_extension_get_property;
	obj_class->set_property = xeme_extension_set_property;

	/**
	 * XemeExtension:element:
	 *
	 * The name of the element that's being extended.
	 *
	 * Since: 0.1
	 */
	properties[PROP_ELEMENT] = g_param_spec_string(
		"element", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeExtension:namespace:
	 *
	 * The namespace of the extension.
	 *
	 * Since: 0.1
	 */
	properties[PROP_NAMESPACE] = g_param_spec_string(
		"namespace", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
const char *
xeme_extension_get_element(XemeExtension *extension) {
	XemeExtensionPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_EXTENSION(extension), NULL);

	priv = xeme_extension_get_instance_private(extension);

	return priv->element;
}

const char *
xeme_extension_get_namespace(XemeExtension *extension) {
	XemeExtensionPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_EXTENSION(extension), NULL);

	priv = xeme_extension_get_instance_private(extension);

	return priv->namespace;
}

void
xeme_extension_parse_start(XemeExtension *extension, XemeInputStream *input,
                           GMarkupParseContext *context)
{
	XemeExtensionClass *klass = NULL;

	g_return_if_fail(XEME_IS_EXTENSION(extension));
	g_return_if_fail(XEME_IS_INPUT_STREAM(input));
	g_return_if_fail(context != NULL);

	klass = XEME_EXTENSION_GET_CLASS(extension);
	if(klass != NULL && klass->parse_start != NULL) {
		klass->parse_start(extension, input, context);
	}
}

void
xeme_extension_parse_end(XemeExtension *extension,
                         GMarkupParseContext *context)
{
	XemeExtensionClass *klass = NULL;

	g_return_if_fail(XEME_IS_EXTENSION(extension));
	g_return_if_fail(context != NULL);

	klass = XEME_EXTENSION_GET_CLASS(extension);
	if(klass != NULL && klass->parse_end != NULL) {
		klass->parse_end(extension, context);
	}
}

char *
xeme_extension_serialize(XemeExtension *extension) {
	XemeExtensionClass *klass = NULL;

	g_return_val_if_fail(XEME_IS_EXTENSION(extension), NULL);

	klass = XEME_EXTENSION_GET_CLASS(extension);
	if(klass != NULL && klass->serialize != NULL) {
		return klass->serialize(extension);
	}

	return NULL;
}
0707010000002B000081A400000000000000000000000167AEF51100000E64000000000000000000000000000000000000001F00000000xeme-0+53/xeme/xemeextension.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_EXTENSION_H
#define XEME_EXTENSION_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XemeExtension:
 *
 * The base class for
 * [extension elements](https://datatracker.ietf.org/doc/html/rfc6120#section-8.4).
 *
 * Since: 0.1
 */

#define XEME_TYPE_EXTENSION (xeme_extension_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_DERIVABLE_TYPE(XemeExtension, xeme_extension, XEME, EXTENSION,
                         GObject)

#include "xemeinputstream.h"

struct _XemeExtensionClass {
	/*< private >*/
	GObjectClass parent;

	/*< public >*/
	void (*parse_start)(XemeExtension *extension, XemeInputStream *input, GMarkupParseContext *context);
	void (*parse_end)(XemeExtension *extension, GMarkupParseContext *context);
	char *(*serialize)(XemeExtension *extension);

	/*< private >*/
	gpointer reserved[8];
};

/**
 * xeme_extension_get_element:
 * @extension: The instance.
 *
 * Gets the name of the element from @extension.
 *
 * Returns: The name of the element of the extension.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_extension_get_element(XemeExtension *extension);

/**
 * xeme_extension_get_namespace:
 * @extension: The instance.
 *
 * Gets the namespace from @extension.
 *
 * Returns: The namespace of the extension.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_extension_get_namespace(XemeExtension *extension);

/**
 * xeme_extension_parse_start:
 * @extension: The instance.
 * @input: The [class@XemeInputStream] that is being parsed.
 * @context: The [struct@GLib.MarkupParseContext] of the parser.
 *
 * Starts parsing an extension.
 *
 * This calls [vfunc@Extension.parse_start] which should use @context in a call
 * to [method@GLib.MarkupParseContext.push] with its own parser to parse the
 * data and set @extension appropriately.
 *
 * Note: This method should be called from a start_element implementation of a
 * [struct@GLib.MarkupParser].
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_extension_parse_start(XemeExtension *extension, XemeInputStream *input, GMarkupParseContext *context);

/**
 * xeme_extension_parse_end:
 * @extension: The instance.
 * @context: The [struct@GLib.MarkupParseContext] of the parser.
 *
 * Finishes a previous call to [method@XemeExtension.parse_start].
 *
 * Note: This method should be called from an end_element implementation of a
 * [struct@GLib.MarkupParser].
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_extension_parse_end(XemeExtension *extension, GMarkupParseContext *context);

/**
 * xeme_extension_serialize:
 * @extension: The instance.
 *
 * Serializes @extension into XML.
 *
 * This calls [vfunc@Extension.serialize] to do the actual serialization.
 *
 * Returns: (transfer full): The XML data as a string for @extension.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
char *xeme_extension_serialize(XemeExtension *extension);

G_END_DECLS

#endif /* XEME_EXTENSION_H */

0707010000002C000081A400000000000000000000000167AEF51100001E80000000000000000000000000000000000000002200000000xeme-0+53/xeme/xemeextensionmap.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeextensionmap.h"

#include "xemestring.h"

#define XEME_EXTENSION_MAP_DELIMITER ("@")

typedef struct {
	XemeExtensionMap *map;
	XemeInputStream *input;
	XemeExtension *extension;
} XemeExtensionMapParseData;

struct _XemeExtensionMap {
	GObject parent;

	GHashTable *extensions;
};

G_DEFINE_FINAL_TYPE(XemeExtensionMap, xeme_extension_map, G_TYPE_OBJECT)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static XemeExtensionMapParseData *
xeme_extension_map_parse_data_new(XemeExtensionMap *map,
                                  XemeInputStream *input)
{
	XemeExtensionMapParseData *data = NULL;

	data = g_new(XemeExtensionMapParseData, 1);
	data->map = g_object_ref(map);
	data->input = g_object_ref(input);
	data->extension = NULL;

	return data;
}

static void
xeme_extension_map_parse_data_free(XemeExtensionMapParseData *data) {
	g_return_if_fail(data != NULL);

	g_clear_object(&data->map);
	g_clear_object(&data->input);
	g_clear_object(&data->extension);

	g_clear_pointer(&data, g_free);
}

static inline char *
xeme_extension_map_generate_key(const char *element, const char *namespace) {
	return g_strconcat(element, XEME_EXTENSION_MAP_DELIMITER, namespace, NULL);
}

static inline char *
xeme_extension_map_generate_key_from_extension(XemeExtension *extension) {
	const char *element = NULL;
	const char *namespace = NULL;

	element = xeme_extension_get_element(extension);
	g_return_val_if_fail(!xeme_str_is_empty(element), NULL);

	namespace = xeme_extension_get_namespace(extension);
	g_return_val_if_fail(!xeme_str_is_empty(namespace), NULL);

	return xeme_extension_map_generate_key(element, namespace);
}

/******************************************************************************
 * Parser
 *****************************************************************************/
static void
xeme_extension_map_parser_element_start(GMarkupParseContext *context,
                                        const char *element_name,
                                        const char **attribute_names,
                                        const char **attribute_values,
                                        gpointer user_data,
                                        GError **error)
{
	XemeExtensionMapParseData *data = user_data;
	XemeExtension *extension = NULL;
	gboolean ret = FALSE;
	const char *namespace = NULL;

	ret = g_markup_collect_attributes(
		element_name, attribute_names, attribute_values, error,
		G_MARKUP_COLLECT_STRING, "xmlns", &namespace,
		G_MARKUP_COLLECT_INVALID);

	if(!ret) {
		return;
	}

	extension = xeme_extension_map_lookup(data->map, element_name, namespace);
	if(!XEME_IS_EXTENSION(extension)) {
		g_message("unknown extension element '%s' with namespace '%s'",
		          element_name, namespace);
		return;
	}

	g_set_object(&data->extension, extension);
	xeme_extension_parse_start(extension, data->input, context);
}

static void
xeme_extension_map_parser_element_end(GMarkupParseContext *context,
                                      G_GNUC_UNUSED const char *element_name,
                                      gpointer user_data,
                                      G_GNUC_UNUSED GError **error)
{
	XemeExtensionMapParseData *data = user_data;

	/* data->extension will be null for extensions we don't know about. */
	if(!XEME_IS_EXTENSION(data->extension)) {
		return;
	}

	xeme_extension_parse_end(data->extension, context);
	g_clear_object(&data->extension);
}

static GMarkupParser parser = {
	.start_element = xeme_extension_map_parser_element_start,
	.end_element = xeme_extension_map_parser_element_end,
};

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_extension_map_finalize(GObject *obj) {
	XemeExtensionMap *map = XEME_EXTENSION_MAP(obj);

	g_clear_pointer(&map->extensions, g_hash_table_unref);

	G_OBJECT_CLASS(xeme_extension_map_parent_class)->finalize(obj);
}

static void
xeme_extension_map_init(XemeExtensionMap *map) {
	map->extensions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
	                                        g_object_unref);
}

static void
xeme_extension_map_class_init(XemeExtensionMapClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_extension_map_finalize;
}

/******************************************************************************
 * Public API
 *****************************************************************************/
XemeExtensionMap *
xeme_extension_map_new(void) {
	return g_object_new(XEME_TYPE_EXTENSION_MAP, NULL);
}

gboolean
xeme_extension_map_add(XemeExtensionMap *map, XemeExtension *extension) {
	char *key = NULL;

	g_return_val_if_fail(XEME_IS_EXTENSION_MAP(map), FALSE);
	g_return_val_if_fail(XEME_IS_EXTENSION(extension), FALSE);

	key = xeme_extension_map_generate_key_from_extension(extension);
	g_return_val_if_fail(key != NULL, FALSE);

	return g_hash_table_insert(map->extensions, key, g_object_ref(extension));
}

gboolean
xeme_extension_map_remove(XemeExtensionMap *map, XemeExtension *extension) {
	char *key = NULL;
	gboolean ret = FALSE;

	g_return_val_if_fail(XEME_IS_EXTENSION_MAP(map), FALSE);
	g_return_val_if_fail(XEME_IS_EXTENSION(extension), FALSE);

	key = xeme_extension_map_generate_key_from_extension(extension);
	g_return_val_if_fail(key != NULL, FALSE);

	ret = g_hash_table_remove(map->extensions, key);
	g_free(key);

	return ret;
}

XemeExtension *
xeme_extension_map_lookup(XemeExtensionMap *map, const char *element,
                          const char *namespace)
{
	XemeExtension *extension = NULL;
	char *key = NULL;

	g_return_val_if_fail(XEME_IS_EXTENSION_MAP(map), NULL);
	g_return_val_if_fail(!xeme_str_is_empty(element), NULL);
	g_return_val_if_fail(!xeme_str_is_empty(namespace), NULL);

	key = xeme_extension_map_generate_key(element, namespace);
	extension = g_hash_table_lookup(map->extensions, key);
	g_free(key);

	return extension;
}

guint
xeme_extension_map_get_size(XemeExtensionMap *map) {
	g_return_val_if_fail(XEME_IS_EXTENSION_MAP(map), 0);

	return g_hash_table_size(map->extensions);
}

void
xeme_extension_map_parse_start(XemeExtensionMap *map, XemeInputStream *input,
                               GMarkupParseContext *context)
{
	XemeExtensionMapParseData *data = NULL;

	g_return_if_fail(XEME_IS_EXTENSION_MAP(map));
	g_return_if_fail(XEME_IS_INPUT_STREAM(input));
	g_return_if_fail(context != NULL);

	data = xeme_extension_map_parse_data_new(map, input);

	g_markup_parse_context_push(context, &parser, data);
}

void
xeme_extension_map_parse_end(XemeExtensionMap *map,
                             GMarkupParseContext *context)
{
	XemeExtensionMapParseData *data = NULL;

	g_return_if_fail(XEME_IS_EXTENSION_MAP(map));
	g_return_if_fail(context != NULL);

	data = g_markup_parse_context_pop(context);

	xeme_extension_map_parse_data_free(data);
}
0707010000002D000081A400000000000000000000000167AEF5110000110B000000000000000000000000000000000000002200000000xeme-0+53/xeme/xemeextensionmap.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_EXTENSION_MAP_H
#define XEME_EXTENSION_MAP_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include "xemeextension.h"
#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XemeExtensionMap:
 *
 * A collection for keeping track of and handling
 * [extension elements](https://datatracker.ietf.org/doc/html/rfc6120#section-8.4).
 *
 * Since: 0.1
 */

#define XEME_TYPE_EXTENSION_MAP (xeme_extension_map_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_FINAL_TYPE(XemeExtensionMap, xeme_extension_map, XEME, EXTENSION_MAP,
                     GObject)

/**
 * xeme_extension_map_new:
 *
 * Creates a new extension map.
 *
 * Returns: (transfer full): The new instance.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeExtensionMap *xeme_extension_map_new(void);

/**
 * xeme_extension_map_add:
 * @map: The instance.
 * @extension: The extension to add.
 *
 * Adds @extension to @map.
 *
 * Returns: %TRUE if @extension was not already in @map, otherwise %FALSE.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_extension_map_add(XemeExtensionMap *map, XemeExtension *extension);

/**
 * xeme_extension_map_remove:
 * @map: The instance.
 * @extension: The extension to remove.
 *
 * Removes @extension from @map.
 *
 * Returns: %TRUE if @extension was removed from @map, otherwise %FALSE.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_extension_map_remove(XemeExtensionMap *map, XemeExtension *extension);

/**
 * xeme_extension_map_lookup:
 * @map: The instance.
 * @element: (not nullable): The name of the element of the extension.
 * @namespace: The namespace of the extension.
 *
 * Look if an extension matching @element and @namespace has been added to
 * @map.
 *
 * Returns: (transfer none): The extension if found, otherwise %NULL.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeExtension *xeme_extension_map_lookup(XemeExtensionMap *map, const char *element, const char *namespace);

/**
 * xeme_extension_map_get_size:
 * @map: The instance.
 *
 * Gets the number of extensions that @map knows about.
 *
 * Returns: The number of extensions added to @map.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
guint xeme_extension_map_get_size(XemeExtensionMap *map);

/**
 * xeme_extension_map_parse_start:
 * @map: The instance.
 * @input: The [class@InputStream] that is being parsed.
 * @context: The [struct@GLib.MarkupParseContext] of the parser.
 *
 * Walks through all of the elements in @context. If extension elements are
 * found in @map, then their parse methods will be called and processed.
 *
 * This should only be called from a start_element implementation of a
 * [struct@GLib.MarkupParser].
 *
 * Note: This only processes extension elements, if you have mixed context, you
 * will need to use [method@ExtensionMap.lookup] to determine if an element is
 * an extension element and then call the [method@Extension.parse_start] in
 * the start_element implementation as well as the [method@Extension.parse_end]
 * in the end_element implementation.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_extension_map_parse_start(XemeExtensionMap *map, XemeInputStream *input, GMarkupParseContext *context);

/**
 * xeme_extension_map_parse_end:
 * @map: The instance.
 * @context: The [struct@GLib.MarkupParseContext] for the parser.
 *
 * Finishes a previous call to [method@ExtensionMap.parse_start].
 *
 * This method should be called from an end_element implementation of a
 * [struct@GLib.MarkupParser].
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_extension_map_parse_end(XemeExtensionMap *map, GMarkupParseContext *context);

G_END_DECLS

#endif /* XEME_EXTENSION_MAP_H */
0707010000002E000081A400000000000000000000000167AEF51100002D1B000000000000000000000000000000000000002100000000xeme-0+53/xeme/xemeinputstream.c/*
 * Copyright (C) 2023-2024 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeinputstream.h"

#include "xemecore.h"
#include "xemeextensionmap.h"
#include "xemestring.h"

const gsize XEME_INPUT_STREAM_BUFFER_LEN = 4096;

enum {
	PROP_0,
	PROP_OUTPUT_STREAM,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

enum {
	SIG_CLOSED,
	SIG_RESTART_REQUESTED,
	N_SIGNALS,
};
static guint signals[N_SIGNALS] = {0, };

typedef struct {
	gboolean running;

	GInputStream *input;
	XemeOutputStream *output;
	GMarkupParseContext *context;

	XemeExtensionMap *features;
} XemeInputStreamPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(XemeInputStream, xeme_input_stream,
                           XEME_TYPE_STREAM)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
xeme_input_stream_close(XemeInputStream *stream,
                        G_GNUC_UNUSED GError **error)
{
	g_signal_emit(stream, signals[SIG_CLOSED], 0);
}

static void
xeme_input_stream_read_bytes_async_cb(G_GNUC_UNUSED GObject *source,
                                      GAsyncResult *result, gpointer data)
{
	XemeInputStream *stream = data;
	XemeInputStreamPrivate *priv = NULL;
	GBytes *bytes = NULL;
	GError *error = NULL;
	const char *raw = NULL;
	gsize length = 0;

	priv = xeme_input_stream_get_instance_private(stream);

	bytes = g_input_stream_read_bytes_finish(priv->input, result, &error);
	if(bytes == NULL) {
		g_message("failed to read: %s",
		          error != NULL ? error->message : "unknown error");
		g_clear_error(&error);

		xeme_input_stream_close(stream, NULL);

		return;
	}

	raw = g_bytes_get_data(bytes, &length);
	if(!g_markup_parse_context_parse(priv->context, raw, length, &error)) {
		g_warning("we hit an error: %s", error->message);
		length = 0;
	}

	/* Unref the data we read. */
	g_bytes_unref(bytes);

	/* Make another asynchronous read call. */
	if(length > 0 && priv->running == TRUE) {
		GCancellable *cancellable = NULL;

		cancellable = xeme_stream_get_cancellable(XEME_STREAM(stream));

		g_input_stream_read_bytes_async(priv->input,
		                                XEME_INPUT_STREAM_BUFFER_LEN,
		                                G_PRIORITY_DEFAULT, cancellable,
		                                xeme_input_stream_read_bytes_async_cb,
		                                stream);
	}
}

/******************************************************************************
 * Parser
 *****************************************************************************/
static void
xeme_input_stream_parse_stream(XemeInputStream *input_stream,
                               G_GNUC_UNUSED const char *element_name,
                               const char **attribute_names,
                               const char **attribute_values,
                               GError **error)
{
	XemeStream *stream = XEME_STREAM(input_stream);
	gboolean ret = FALSE;
	const char *id = NULL;
	const char *to = NULL;
	const char *from = NULL;
	const char *version = NULL;
	const char *xml_lang = NULL;
	const char *xmlns = NULL;
	const char *xmlns_stream = NULL;

	ret = g_markup_collect_attributes(
		element_name, attribute_names, attribute_values, error,
		G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "id", &id,
		G_MARKUP_COLLECT_STRING, "to", &to,
		G_MARKUP_COLLECT_STRING, "from", &from,
		G_MARKUP_COLLECT_STRING, "version", &version,
		G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xml:lang", &xml_lang,
		G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns", &xmlns,
		G_MARKUP_COLLECT_STRING | G_MARKUP_COLLECT_OPTIONAL, "xmlns:stream", &xmlns_stream,
	    G_MARKUP_COLLECT_INVALID);

	if(!ret) {
		return;
	}

	if(!xeme_str_is_empty(id)) {
		xeme_stream_set_id(stream, id);
	}

	xeme_stream_set_to(stream, to);
	xeme_stream_set_from(stream, from);

	if(!xeme_str_equal(version, XEME_STREAM_VERSION)) {
		g_set_error(error, XEME_DOMAIN, 0,
		            "got version='%s' but we were expecting '%s'",
		            version, XEME_STREAM_VERSION);
		return;
	}

	if(!xeme_str_is_empty(xml_lang)) {
		xeme_stream_set_language(stream, xml_lang);
	}

	if(!xeme_str_equal(xmlns, XEME_STREAM_XMLNS)) {
		g_set_error(error, XEME_DOMAIN, 0,
		            "got xmlns='%s' but was expecting '%s'",
		            xmlns, XEME_STREAM_XMLNS);
		return;
	}

	if(!xeme_str_equal(xmlns_stream, XEME_STREAM_XMLNS_STREAM)) {
		g_set_error(error, XEME_DOMAIN, 0,
		            "got xmlns:stream='%s' but was expecting '%s'",
		            xmlns_stream, XEME_STREAM_XMLNS_STREAM);
		return;
	}
}

static void
xeme_connection_parser_element_start(G_GNUC_UNUSED GMarkupParseContext *context,
                                     const char *name,
                                     const char **attribute_names,
                                     const char **attribute_values,
                                     gpointer data,
                                     GError **error)
{
	XemeInputStream *stream = data;
	XemeInputStreamPrivate *priv = NULL;

	priv = xeme_input_stream_get_instance_private(stream);

	if(xeme_str_equal(name, "stream:stream")) {
		xeme_input_stream_parse_stream(stream, name, attribute_names,
		                               attribute_values, error);
	} else if(xeme_str_equal(name, "stream:features")) {
		xeme_extension_map_parse_start(priv->features, stream, context);
	} else {
		g_warning("unexpected element start '%s'", name);
	}
}

static void
xeme_connection_parser_element_end(G_GNUC_UNUSED GMarkupParseContext *context,
                                   const char *name,
                                   gpointer data,
                                   GError **error)
{
	XemeInputStream *stream = data;

	if(xeme_str_equal(name, "stream:stream")) {
		/* The connection is being terminated. */
		xeme_input_stream_close(stream, error);

		return;
	} else {
		g_warning("unexpected element end '%s'", name);
	}
}

static GMarkupParser parser = {
	.start_element = xeme_connection_parser_element_start,
	.end_element = xeme_connection_parser_element_end,
};

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_input_stream_finalize(GObject *obj) {
	XemeInputStream *stream = XEME_INPUT_STREAM(obj);
	XemeInputStreamPrivate *priv = NULL;

	priv = xeme_input_stream_get_instance_private(stream);

	g_clear_pointer(&priv->context, g_markup_parse_context_free);
	g_clear_object(&priv->output);
	g_clear_object(&priv->features);

	G_OBJECT_CLASS(xeme_input_stream_parent_class)->finalize(obj);
}

static void
xeme_input_stream_get_property(GObject *obj, guint param_id, GValue *value,
                               GParamSpec *pspec)
{
	XemeInputStream *stream = XEME_INPUT_STREAM(obj);

	switch(param_id) {
	case PROP_OUTPUT_STREAM:
		g_value_set_object(value, xeme_input_stream_get_output_stream(stream));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_input_stream_init(XemeInputStream *stream) {
	XemeInputStreamPrivate *priv = NULL;

	priv = xeme_input_stream_get_instance_private(stream);
	priv->running = FALSE;

	priv->context = g_markup_parse_context_new(&parser, 0, stream, NULL);

	priv->features = xeme_extension_map_new();
}

static void
xeme_input_stream_class_init(XemeInputStreamClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_input_stream_finalize;
	obj_class->get_property = xeme_input_stream_get_property;

	/**
	 * XemeInputStream:output-stream:
	 *
	 * The output stream that should be used for requests and responses.
	 *
	 * This is only set after [method@InputStream.start] is called.
	 *
	 * Since: 0.1
	 */
	properties[PROP_OUTPUT_STREAM] = g_param_spec_object(
		"output-stream", NULL, NULL,
		XEME_TYPE_OUTPUT_STREAM,
		G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);

	/**
	 * XemeInputStream::closed:
	 *
	 * Emitted when the input stream is closed.
	 *
	 * Since: 0.1
	 */
	signals[SIG_CLOSED] = g_signal_new_class_handler(
		"closed",
		G_OBJECT_CLASS_TYPE(klass),
		G_SIGNAL_RUN_LAST,
		NULL,
		NULL,
		NULL,
		NULL,
		G_TYPE_NONE,
		0);

	/**
	 * XemeInputStream::restart-requested:
	 *
	 * Emitted when the remote side has requested that the output stream be
	 * restarted.
	 *
	 * This is typically requested during feature negotiation which happens
	 * during the initial connection.
	 *
	 * Since: 0.1
	 */
	signals[SIG_RESTART_REQUESTED] = g_signal_new_class_handler(
		"restart-requested",
		G_OBJECT_CLASS_TYPE(klass),
		G_SIGNAL_RUN_LAST,
		NULL,
		NULL,
		NULL,
		NULL,
		G_TYPE_NONE,
		0);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
XemeInputStream *
xeme_input_stream_new(void) {
	return g_object_new(XEME_TYPE_INPUT_STREAM, NULL);
}

gboolean
xeme_input_stream_start(XemeInputStream *stream, GInputStream *input,
                        XemeOutputStream *output, GError **error)
{
	XemeInputStreamPrivate *priv = NULL;
	GCancellable *cancellable = NULL;

	g_return_val_if_fail(XEME_IS_INPUT_STREAM(stream), FALSE);
	g_return_val_if_fail(G_IS_INPUT_STREAM(input), FALSE);
	g_return_val_if_fail(XEME_IS_OUTPUT_STREAM(output), FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

	priv = xeme_input_stream_get_instance_private(stream);

	if(priv->running) {
		g_set_error_literal(error, XEME_DOMAIN, 0,
		                    "input stream has already been started");

		return FALSE;
	}

	cancellable = xeme_stream_get_cancellable(XEME_STREAM(stream));

	/* Store the output stream. */
	if(g_set_object(&priv->output, output)) {
		g_object_notify_by_pspec(G_OBJECT(stream),
		                         properties[PROP_OUTPUT_STREAM]);
	}

	/* Setup the read handler. */
	priv->input = g_object_ref(input);
	g_input_stream_read_bytes_async(priv->input,
	                                XEME_INPUT_STREAM_BUFFER_LEN,
	                                G_PRIORITY_DEFAULT, cancellable,
	                                xeme_input_stream_read_bytes_async_cb,
	                                stream);

	priv->running = TRUE;

	return TRUE;
}

void
xeme_input_stream_restart_requested(XemeInputStream *stream) {
	g_return_if_fail(XEME_IS_INPUT_STREAM(stream));

	g_signal_emit(stream, signals[SIG_RESTART_REQUESTED], 0);
}

XemeOutputStream *
xeme_input_stream_get_output_stream(XemeInputStream *stream) {
	XemeInputStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_INPUT_STREAM(stream), NULL);

	priv = xeme_input_stream_get_instance_private(stream);

	return priv->output;
}
0707010000002F000081A400000000000000000000000167AEF51100000E31000000000000000000000000000000000000002100000000xeme-0+53/xeme/xemeinputstream.h/*
 * Copyright (C) 2023-2024 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_INPUT_STREAM_H
#define XEME_INPUT_STREAM_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include <xeme/xememessage.h>
#include <xeme/xemeoutputstream.h>
#include <xeme/xemestream.h>
#include <xeme/xemeversion.h>

G_BEGIN_DECLS

/**
 * XemeInputStream:
 *
 * An object that handles the incoming stream from a remote connection.
 *
 * Since: 0.1
 */

#define XEME_TYPE_INPUT_STREAM (xeme_input_stream_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_DERIVABLE_TYPE(XemeInputStream, xeme_input_stream, XEME, INPUT_STREAM, XemeStream)

struct _XemeInputStreamClass {
	/*< private >*/
	XemeStreamClass parent;

	/*< private >*/
	gpointer reserved[8];
};

/**
 * xeme_input_stream_new:
 *
 * Creates a new input stream.
 *
 * Returns: (transfer full): The new instance.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeInputStream *xeme_input_stream_new(void);

/**
 * xeme_input_stream_start:
 * @stream: The instance.
 * @input: (transfer none): A [class@Gio.InputStream] for reading data from the
 *         server.
 * @output: (transfer none): The output stream for sending data.
 * @error: (nullable): A return address for a #GError.
 *
 * Starts processing @input as an incoming XMPP stream. It is the
 * responsibility of the caller to establish any necessary connections as this
 * just processes the raw XMPP data from @input.
 *
 * Input is handled asynchronously so this function will always return
 * immediately. If setup and processing was successful, %TRUE will be returned,
 * otherwise %FALSE will be returned with @error set.
 *
 * Returns: %TRUE on success, otherwise %FALSE with @error set.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_input_stream_start(XemeInputStream *stream, GInputStream *input, XemeOutputStream *output, GError **error);

/**
 * xeme_input_stream_restart_requested:
 * @stream: The instance.
 *
 * Emits the [signal@InputStream::restart-requested] signal.
 *
 * This should be called when the remote side has requested that the output
 * stream should be restarted. This typically happens during feature
 * negotiation during the initial connection.
 *
 * This signal allows who ever is managing the streams to call
 * [method@OutputStream.restart] on the output stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_input_stream_restart_requested(XemeInputStream *stream);

/**
 * xeme_input_stream_get_output_stream:
 * @stream: The instance.
 *
 * Gets the [class@OutputStream] that should be used for requests and responses
 * with the remote end of the connection.
 *
 * Note: this will only be set after [method@InputStream.start] has been
 * called.
 *
 * Returns: (transfer none) (nullable): The output stream if set, otherwise
 *          %NULL.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeOutputStream *xeme_input_stream_get_output_stream(XemeInputStream *stream);

G_END_DECLS

#endif /* XEME_INPUT_STREAM_H */
07070100000030000081A400000000000000000000000167AEF5110000264A000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xememessage.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xememessage.h"
#include "xemestring.h"

enum {
	PROP_0,
	PROP_THREAD,
	PROP_THREAD_PARENT,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

struct _XemeMessage {
	XemeStanza parent;

	GHashTable *bodies;
	GPtrArray *bodies_order;

	GHashTable *subjects;
	GPtrArray *subjects_order;

	char *thread;
	char *thread_parent;
};

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
xeme_message_add_element_with_language(XemeMessage *message,
                                       GHashTable *collection,
                                       GPtrArray *order,
                                       const char *language,
                                       const char *content)
{
	char *lang = NULL;
	gboolean found = FALSE;
	gpointer key = NULL;

	g_return_if_fail(XEME_IS_MESSAGE(message));
	g_return_if_fail(content != NULL);

	/* If the caller passes in NULL, we just convert it to empty string. */
	if(!xeme_str_is_empty(language)) {
		lang = g_strdup(language);
	} else {
		lang = g_strdup("");
	}

	/* Look if the language already exists, and if so, remove it from our
	 * ordered list of languages.
	 */
	found = g_hash_table_lookup_extended(collection, lang, &key, NULL);
	if(found) {
		g_ptr_array_remove(order, key);
	}

	/* Add the language to our list of ordered languages. This is used to look
	 * up keys in the bodies hash table, so we just keep a pointer to that
	 * value rather than creating a new copy and requiring a strcmp to find it
	 * again.
	 */
	g_ptr_array_add(order, lang);

	/* We use g_hash_table_replace because we want to free the old key and
	 * have it use the new one we pass in, which is the opposite of what
	 * g_hash_table_insert does.
	 */
	g_hash_table_replace(collection, lang, g_strdup(content));
}

static void
xeme_message_marshal_language_collection(GHashTable *collection,
                                         GPtrArray *order,
                                         GString *str,
                                         const char *element)
{
	for(guint i = 0; i < order->len; i++) {
		const char *lang = NULL;
		const char *content = NULL;

		lang = g_ptr_array_index(order, i);
		content = g_hash_table_lookup(collection, lang);

		g_string_append_printf(str, "<%s", element);
		if(xeme_str_is_empty(lang)) {
			g_string_append_printf(str, ">%s", content);
		} else {
			g_string_append_printf(str, " xml:lang=\"%s\">%s", lang, content);
		}
		g_string_append_printf(str, "</%s>", element);
	}
}

/******************************************************************************
 * XemeStanza Implementation
 *****************************************************************************/
static char *
xeme_message_marshal(XemeStanza *stanza, G_GNUC_UNUSED GError **error) {
	XemeMessage *message = XEME_MESSAGE(stanza);
	GString *str = NULL;
	char *attrs = NULL;

	str = g_string_new("<message");

	attrs = xeme_stanza_marshal_attributes(stanza);
	if(!xeme_str_is_empty(attrs)) {
		g_string_append_printf(str, " %s>", attrs);
	} else {
		g_string_append(str, ">");
	}
	g_clear_pointer(&attrs, g_free);

	/* Now run through the collections. */
	xeme_message_marshal_language_collection(message->subjects,
	                                         message->subjects_order, str,
	                                         "subject");
	xeme_message_marshal_language_collection(message->bodies,
	                                         message->bodies_order, str,
	                                         "body");

	if(!xeme_str_is_empty(message->thread)) {
		if(!xeme_str_is_empty(message->thread_parent)) {
			g_string_append_printf(str, "<thread parent=\"%s\">%s</thread>",
			                       message->thread_parent, message->thread);
		} else {
			g_string_append_printf(str, "<thread>%s</thread>",
			                       message->thread);
		}
	}

	/* Finish up */
	g_string_append(str, "</message>");

	return g_string_free_and_steal(str);
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_FINAL_TYPE(XemeMessage, xeme_message, XEME_TYPE_STANZA)

static void
xeme_message_finalize(GObject *obj) {
	XemeMessage *message = XEME_MESSAGE(obj);

	g_ptr_array_free(message->subjects_order, TRUE);
	message->subjects_order = NULL;
	g_clear_pointer(&message->subjects, g_hash_table_destroy);

	g_ptr_array_free(message->bodies_order, TRUE);
	message->bodies_order = NULL;
	g_clear_pointer(&message->bodies, g_hash_table_destroy);

	g_clear_pointer(&message->thread, g_free);
	g_clear_pointer(&message->thread_parent, g_free);

	G_OBJECT_CLASS(xeme_message_parent_class)->finalize(obj);
}

static void
xeme_message_get_property(GObject *obj, guint param_id, GValue *value,
                          GParamSpec *pspec)
{
	XemeMessage *message = XEME_MESSAGE(obj);

	switch(param_id) {
	case PROP_THREAD:
		g_value_set_string(value, xeme_message_get_thread(message));
		break;
	case PROP_THREAD_PARENT:
		g_value_set_string(value, xeme_message_get_thread_parent(message));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_message_set_property(GObject *obj, guint param_id, const GValue *value,
                          GParamSpec *pspec)
{
	XemeMessage *message = XEME_MESSAGE(obj);

	switch(param_id) {
	case PROP_THREAD:
		xeme_message_set_thread(message, g_value_get_string(value));
		break;
	case PROP_THREAD_PARENT:
		xeme_message_set_thread_parent(message, g_value_get_string(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_message_init(XemeMessage *message) {
	message->subjects = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
	                                          g_free);
	message->subjects_order = g_ptr_array_new();

	message->bodies = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
	                                        g_free);
	message->bodies_order = g_ptr_array_new();
}

static void
xeme_message_class_init(XemeMessageClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
	XemeStanzaClass *stanza_class = XEME_STANZA_CLASS(klass);

	obj_class->finalize = xeme_message_finalize;
	obj_class->get_property = xeme_message_get_property;
	obj_class->set_property = xeme_message_set_property;

	stanza_class->marshal = xeme_message_marshal;

	/**
	 * XemeMessage:thread:
	 *
	 * The thread identifier for this message.
	 *
	 * Since: 0.1
	 */
	properties[PROP_THREAD] = g_param_spec_string(
		"thread", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeMessage:thread-parent:
	 *
	 * The thread parent identifier for this message.
	 *
	 * Since: 0.1
	 */
	properties[PROP_THREAD_PARENT] = g_param_spec_string(
		"thread-parent", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
XemeStanza *
xeme_message_new(void) {
	return g_object_new(XEME_TYPE_MESSAGE, NULL);
}

const char *
xeme_message_get_thread(XemeMessage *message) {
	g_return_val_if_fail(XEME_IS_MESSAGE(message), NULL);

	return message->thread;
}

void
xeme_message_set_thread(XemeMessage *message, const char *thread) {
	g_return_if_fail(XEME_IS_MESSAGE(message));

	if(g_set_str(&message->thread, thread)) {
		g_object_notify_by_pspec(G_OBJECT(message), properties[PROP_THREAD]);
	}
}

const char *
xeme_message_get_thread_parent(XemeMessage *message) {
	g_return_val_if_fail(XEME_IS_MESSAGE(message), NULL);

	return message->thread_parent;
}

void
xeme_message_set_thread_parent(XemeMessage *message,
                               const char *thread_parent)
{
	g_return_if_fail(XEME_IS_MESSAGE(message));

	if(g_set_str(&message->thread_parent, thread_parent)) {
		g_object_notify_by_pspec(G_OBJECT(message),
		                         properties[PROP_THREAD_PARENT]);
	}
}

void
xeme_message_add_body(XemeMessage *message, const char *language,
                      const char *content)
{
	g_return_if_fail(XEME_IS_MESSAGE(message));
	g_return_if_fail(content != NULL);

	xeme_message_add_element_with_language(message, message->bodies,
	                                       message->bodies_order, language,
	                                       content);
}

void
xeme_message_add_subject(XemeMessage *message, const char *language,
                         const char *content)
{
	g_return_if_fail(XEME_IS_MESSAGE(message));
	g_return_if_fail(content != NULL);

	xeme_message_add_element_with_language(message, message->subjects,
	                                       message->subjects_order, language,
	                                       content);
}
07070100000031000081A400000000000000000000000167AEF51100000CF6000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xememessage.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_MESSAGE_H
#define XEME_MESSAGE_H

#include <glib.h>
#include <glib-object.h>

#include "xemestanza.h"
#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XemeMessage:
 *
 * An XMPP Message stanza.
 *
 * Since: 0.1
 */

#define XEME_TYPE_MESSAGE (xeme_message_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_FINAL_TYPE(XemeMessage, xeme_message, XEME, MESSAGE, XemeStanza)

/**
 * xeme_message_new:
 *
 * Creates a new [class@Message] instance.
 *
 * returns: (transfer full) (type XemeMessage): The new message.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeStanza *xeme_message_new(void);

/**
 * xeme_message_get_thread:
 * @message: The instance.
 *
 * Gets the thread identifier of @message.
 *
 * Returns: (transfer none) (nullable): The thread id.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_message_get_thread(XemeMessage *message);

/**
 * xeme_message_set_thread:
 * @message: The instance.
 * @thread: (nullable): The new thread identifier.
 *
 * Sets the thread identifier of @message.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_message_set_thread(XemeMessage *message, const char *thread);

/**
 * xeme_message_get_thread_parent:
 * @message: The instance.
 *
 * Gets the thread parent identifier of @message.
 *
 * Returns: (transfer none) (nullable): The thread id.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_message_get_thread_parent(XemeMessage *message);

/**
 * xeme_message_set_thread_parent:
 * @message: The instance.
 * @thread_parent: (nullable): The new thread parent identifier.
 *
 * Sets the thread parent identifier of @message.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_message_set_thread_parent(XemeMessage *message, const char *thread_parent);

/**
 * xeme_message_add_subject:
 * @message: The instance.
 * @language: (nullable): The value for the xml:lang tag.
 * @content: The content.
 *
 * Add @content as a subject to @message. If a subject already exists with
 * @language, it will be replaced by the new @content.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_message_add_subject(XemeMessage *message, const char *language, const char *content);

/**
 * xeme_message_add_body:
 * @message: The instance.
 * @language: (nullable): The value for the xml:lang tag.
 * @content: The content.
 *
 * Add @content as a body to @message. If a body already exists with @language,
 * it will be replaced by the new @content.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_message_add_body(XemeMessage *message, const char *language, const char *content);

G_END_DECLS

#endif /* XEME_MESSAGE_H */
07070100000032000081A400000000000000000000000167AEF51100001B58000000000000000000000000000000000000002200000000xeme-0+53/xeme/xemeoutputstream.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include <birb.h>

#include "xemecore.h"
#include "xemeoutputstream.h"
#include "xemestring.h"

struct _XemeOutputStream {
	XemeStream parent;

	gboolean running;

	GOutputStream *output;
};

G_DEFINE_FINAL_TYPE(XemeOutputStream, xeme_output_stream, XEME_TYPE_STREAM)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static GBytes *
xeme_output_stream_serialize(XemeOutputStream *output_stream) {
	XemeStream *stream = XEME_STREAM(output_stream);
	GString *str = NULL;
	const char *value = NULL;

	str = g_string_new(XEME_STREAM_PI);
	g_string_append(str, "<stream:stream");

	/* to and from are validated to not be empty in
	 * xeme_output_stream_start. */
	g_string_append_printf(str, " from=\"%s\"", xeme_stream_get_from(stream));
	g_string_append_printf(str, " to=\"%s\"", xeme_stream_get_to(stream));

	value = xeme_stream_get_id(stream);
	if(!xeme_str_is_empty(value)) {
		g_string_append_printf(str, " id=\"%s\"", value);
	}

	value = xeme_stream_get_version(stream);
	if(!xeme_str_is_empty(value)) {
		g_string_append_printf(str, " version=\"%s\"", value);
	}

	value = xeme_stream_get_language(stream);
	if(!xeme_str_is_empty(value)) {
		g_string_append_printf(str, " xml:lang=\"%s\"", value);
	}

	g_string_append_printf(str, " xmlns=\"%s\"", XEME_STREAM_XMLNS);
	g_string_append_printf(str, " xmlns:stream=\"%s\"",
	                       XEME_STREAM_XMLNS_STREAM);
	g_string_append(str, ">");

	/* Create a GBytes with the value from the GString and free the GString
	 * container.
	 */
	return g_string_free_to_bytes(str);
}

/******************************************************************************
 * Writing
 *****************************************************************************/
static void
xeme_output_stream_write_cb(G_GNUC_UNUSED GObject *source, GAsyncResult *res,
                            gpointer data)
{
	XemeOutputStream *stream = data;
	BirbQueuedOutputStream *queued = NULL;
	GError *error = NULL;

	queued = BIRB_QUEUED_OUTPUT_STREAM(stream->output);
	birb_queued_output_stream_push_bytes_finish(queued, res, &error);

	if(error != NULL) {
		/* TODO: We need to propagate this somehow, maybe a signal? */
		g_warning("failed to write data: %s",
		          error->message != NULL ? error->message : "unknown error");
	}
}

static void
xeme_output_stream_write(XemeOutputStream *stream, GBytes *data) {
	BirbQueuedOutputStream *queued = NULL;
	GCancellable *cancellable = NULL;

	g_return_if_fail(XEME_IS_OUTPUT_STREAM(stream));
	g_return_if_fail(data != NULL);

	cancellable = xeme_stream_get_cancellable(XEME_STREAM(stream));
	queued = BIRB_QUEUED_OUTPUT_STREAM(stream->output);

	birb_queued_output_stream_push_bytes_async(queued,
	                                           data,
	                                           G_PRIORITY_DEFAULT,
	                                           cancellable,
	                                           xeme_output_stream_write_cb,
	                                           stream);
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_output_stream_finalize(GObject *obj) {
	XemeOutputStream *stream = XEME_OUTPUT_STREAM(obj);

	g_clear_object(&stream->output);

	G_OBJECT_CLASS(xeme_output_stream_parent_class)->finalize(obj);
}

static void
xeme_output_stream_init(XemeOutputStream *stream) {
	stream->running = FALSE;
}

static void
xeme_output_stream_class_init(XemeOutputStreamClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_output_stream_finalize;
}

/******************************************************************************
 * Public API
 *****************************************************************************/
XemeOutputStream *
xeme_output_stream_new(const char *to, const char *from,
                       GCancellable *cancellable)
{
	return g_object_new(
		XEME_TYPE_OUTPUT_STREAM,
		"cancellable", cancellable,
		"from", from,
		"to", to,
		NULL);
}

gboolean
xeme_output_stream_start(XemeOutputStream *stream, GOutputStream *output,
                         GError **error)
{
	GBytes *data = NULL;
	GOutputStream *wrapper = NULL;

	g_return_val_if_fail(XEME_IS_OUTPUT_STREAM(stream), FALSE);
	g_return_val_if_fail(G_IS_OUTPUT_STREAM(output), FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

	if(stream->running) {
		g_set_error_literal(error, XEME_DOMAIN, 0,
		                    "output stream has already been started");

		return FALSE;
	}

	wrapper = birb_queued_output_stream_new(output);
	if(!g_set_object(&stream->output, wrapper)) {
		g_set_error_literal(error, XEME_DOMAIN, 0,
		                    "failed to set the output stream");

		g_clear_object(&wrapper);

		return FALSE;
	}

	/* The wrapper is referenced by stream->output so we need to get rid of our
	 * local reference which we don't need anymore.
	 */
	g_clear_object(&wrapper);

	data = xeme_output_stream_serialize(stream);
	xeme_output_stream_write(stream, data);
	g_clear_pointer(&data, g_bytes_unref);

	stream->running = TRUE;

	return TRUE;
}

gboolean
xeme_output_stream_restart(XemeOutputStream *stream, GError **error) {
	GBytes *data = NULL;

	g_return_val_if_fail(XEME_IS_OUTPUT_STREAM(stream), FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

	if(!stream->running) {
		g_set_error_literal(error, XEME_DOMAIN, 0,
		                    "output stream has not been started");

		return FALSE;
	}

	data = xeme_output_stream_serialize(stream);
	xeme_output_stream_write(stream, data);
	g_clear_pointer(&data, g_bytes_unref);

	return TRUE;
}

gboolean
xeme_output_stream_stop(XemeOutputStream *stream, GError **error) {
	GCancellable *cancellable = NULL;
	GError *local_error = NULL;
	gboolean ret = TRUE;

	g_return_val_if_fail(XEME_IS_OUTPUT_STREAM(stream), FALSE);

	cancellable = xeme_stream_get_cancellable(XEME_STREAM(stream));

	if(!g_output_stream_close(stream->output, cancellable, &local_error)) {
		g_propagate_error(error, local_error);

		ret = FALSE;
	}

	stream->output = NULL;

	return ret;
}
07070100000033000081A400000000000000000000000167AEF51100000DD3000000000000000000000000000000000000002200000000xeme-0+53/xeme/xemeoutputstream.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_OUTPUT_STREAM_H
#define XEME_OUTPUT_STREAM_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include "xemestream.h"
#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XemeOutputStream:
 *
 * An XMPP output stream. This is used to send stanzas to the other end of the
 * connection.
 *
 * Since: 0.1
 */

#define XEME_TYPE_OUTPUT_STREAM (xeme_output_stream_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_FINAL_TYPE(XemeOutputStream, xeme_output_stream, XEME, OUTPUT_STREAM, XemeStream)

/**
 * xeme_output_stream_new:
 * @to: The jid of the recipient.
 * @from: The jid of the sender.
 * @cancellable: (nullable): A [class@Gio.Cancellable] for the stream.
 *
 * Creates a new output stream.
 *
 * Returns: (transfer full): The new instance.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
XemeOutputStream *xeme_output_stream_new(const char *to, const char *from, GCancellable *cancellable);

/**
 * xeme_output_stream_start:
 * @stream: The instance.
 * @output: (transfer none): A [class@Gio.OutputStream] for writing data to the
 *          server.
 * @error: (nullable): A return address for a #GError.
 *
 * Initiates @output as an outgoing XMPP stream. It is the responsibility of
 * the caller to establish any necessary connections as this writes raw XMPP
 * data to @output.
 *
 * Output is handled asynchronously so this function will always return
 * immediately. If setup and processing was successful, %TRUE will be returned,
 * otherwise %FALSE will be returned with @error set.
 *
 * Returns: %TRUE on success, otherwise %FALSE with @error set.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_output_stream_start(XemeOutputStream *stream, GOutputStream *output, GError **error);

/**
 * xeme_output_stream_restart:
 * @stream: The instance.
 * @error: (nullable): A return address for a #GError.
 *
 * Restarts the stream per
 * [RFC 6120 4.3.3](https://www.rfc-editor.org/rfc/rfc6120#section-4.3.3).
 *
 * This uses the existing connections but will resend the XML Declaration and
 * the initial `stream` element. This is used during the initial negotiation
 * with the server and probably shouldn't be called otherwise.
 *
 * Returns: %TRUE on success, otherwise %FALSE with @error set.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_output_stream_restart(XemeOutputStream *stream, GError **error);

/**
 * xeme_output_stream_stop:
 * @stream: The instance.
 * @error: (nullable): A return address for a #GError.
 *
 * Stops the stream by sending the ending `</stream>` if possible and then
 * closes the output stream.
 *
 * Returns: %TRUE on success, otherwise %FALSE with @error possibly set.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
gboolean xeme_output_stream_stop(XemeOutputStream *stream, GError **error);

G_END_DECLS

#endif /* XEME_OUTPUT_STREAM_H */
07070100000034000081A400000000000000000000000167AEF51100002378000000000000000000000000000000000000001C00000000xeme-0+53/xeme/xemestanza.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemestanza.h"
#include "xemestring.h"

enum {
	PROP_0,
	PROP_CONTENT_TYPE,
	PROP_ID,
	PROP_TO,
	PROP_FROM,
	PROP_XML_LANG,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

typedef struct {
	char *content_type;
	char *id;
	char *to;
	char *from;
	char *xml_lang;
} XemeStanzaPrivate;

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
G_DEFINE_TYPE_WITH_PRIVATE(XemeStanza, xeme_stanza, G_TYPE_OBJECT)

static void
xeme_stanza_finalize(GObject *obj) {
	XemeStanza *stanza = XEME_STANZA(obj);
	XemeStanzaPrivate *priv = xeme_stanza_get_instance_private(stanza);

	g_clear_pointer(&priv->content_type, g_free);
	g_clear_pointer(&priv->from, g_free);
	g_clear_pointer(&priv->id, g_free);
	g_clear_pointer(&priv->to, g_free);
	g_clear_pointer(&priv->xml_lang, g_free);

	G_OBJECT_CLASS(xeme_stanza_parent_class)->finalize(obj);
}

static void
xeme_stanza_get_property(GObject *obj, guint param_id, GValue *value,
                         GParamSpec *pspec)
{
	XemeStanza *stanza = XEME_STANZA(obj);

	switch(param_id) {
	case PROP_CONTENT_TYPE:
		g_value_set_string(value, xeme_stanza_get_content_type(stanza));
		break;
	case PROP_FROM:
		g_value_set_string(value, xeme_stanza_get_from(stanza));
		break;
	case PROP_ID:
		g_value_set_string(value, xeme_stanza_get_id(stanza));
		break;
	case PROP_TO:
		g_value_set_string(value, xeme_stanza_get_to(stanza));
		break;
	case PROP_XML_LANG:
		g_value_set_string(value, xeme_stanza_get_xml_lang(stanza));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_stanza_set_property(GObject *obj, guint param_id, const GValue *value,
                         GParamSpec *pspec)
{
	XemeStanza *stanza = XEME_STANZA(obj);

	switch(param_id) {
	case PROP_CONTENT_TYPE:
		xeme_stanza_set_content_type(stanza, g_value_get_string(value));
		break;
	case PROP_FROM:
		xeme_stanza_set_from(stanza, g_value_get_string(value));
		break;
	case PROP_ID:
		xeme_stanza_set_id(stanza, g_value_get_string(value));
		break;
	case PROP_TO:
		xeme_stanza_set_to(stanza, g_value_get_string(value));
		break;
	case PROP_XML_LANG:
		xeme_stanza_set_xml_lang(stanza, g_value_get_string(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_stanza_init(G_GNUC_UNUSED XemeStanza *stanza) {
}

static void
xeme_stanza_class_init(XemeStanzaClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_stanza_finalize;
	obj_class->get_property = xeme_stanza_get_property;
	obj_class->set_property = xeme_stanza_set_property;

	/**
	 * XemeStanza:content-type:
	 *
	 * The content type of this stanza.
	 *
	 * Since: 0.1
	 */
	properties[PROP_CONTENT_TYPE] = g_param_spec_string(
		"content-type", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStanza:from:
	 *
	 * The sender of this stanza.
	 *
	 * Since: 0.1
	 */
	properties[PROP_FROM] = g_param_spec_string(
		"from", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStanza:id:
	 *
	 * The identifier of this stanza.
	 *
	 * Since: 0.1
	 */
	properties[PROP_ID] = g_param_spec_string(
		"id", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStanza:to:
	 *
	 * The recipient of this stanza.
	 *
	 * Since: 0.1
	 */
	properties[PROP_TO] = g_param_spec_string(
		"to", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStanza:xml-lang:
	 *
	 * The XML Lang value for this stanza.
	 *
	 * Since: 0.1
	 */
	properties[PROP_XML_LANG] = g_param_spec_string(
		"xml-lang", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
const char *
xeme_stanza_get_content_type(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	return priv->content_type;
}

void
xeme_stanza_set_content_type(XemeStanza *stanza, const char *content_type) {
	XemeStanzaPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STANZA(stanza));
	g_return_if_fail(content_type != NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	if(g_set_str(&priv->content_type, content_type)) {
		g_object_notify_by_pspec(G_OBJECT(stanza),
		                         properties[PROP_CONTENT_TYPE]);
	}
}

const char *
xeme_stanza_get_from(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	return priv->from;
}

void
xeme_stanza_set_from(XemeStanza *stanza, const char *from) {
	XemeStanzaPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STANZA(stanza));
	g_return_if_fail(from != NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	if(g_set_str(&priv->from, from)) {
		g_object_notify_by_pspec(G_OBJECT(stanza), properties[PROP_FROM]);
	}
}

const char *
xeme_stanza_get_id(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	return priv->id;
}

void
xeme_stanza_set_id(XemeStanza *stanza, const char *id) {
	XemeStanzaPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STANZA(stanza));
	g_return_if_fail(id != NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	if(g_set_str(&priv->id, id)) {
		g_object_notify_by_pspec(G_OBJECT(stanza), properties[PROP_ID]);
	}
}

const char *
xeme_stanza_get_to(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	return priv->to;
}

void
xeme_stanza_set_to(XemeStanza *stanza, const char *to) {
	XemeStanzaPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STANZA(stanza));
	g_return_if_fail(to != NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	if(g_set_str(&priv->to, to)) {
		g_object_notify_by_pspec(G_OBJECT(stanza),
		                         properties[PROP_TO]);
	}
}

const char *
xeme_stanza_get_xml_lang(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);

	return priv->xml_lang;
}

void
xeme_stanza_set_xml_lang(XemeStanza *stanza, const char *xml_lang) {
	XemeStanzaPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STANZA(stanza));

	priv = xeme_stanza_get_instance_private(stanza);

	if(g_set_str(&priv->xml_lang, xml_lang)) {
		g_object_notify_by_pspec(G_OBJECT(stanza), properties[PROP_XML_LANG]);
	}
}

char *
xeme_stanza_marshal(XemeStanza *stanza, GError **error) {
	XemeStanzaClass *klass = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);
	g_return_val_if_fail(error != NULL && *error == NULL, NULL);

	klass = XEME_STANZA_GET_CLASS(stanza);
	if(klass != NULL && klass->marshal != NULL) {
		return klass->marshal(stanza, error);
	}

	return NULL;
}

char *
xeme_stanza_marshal_attributes(XemeStanza *stanza) {
	XemeStanzaPrivate *priv = NULL;
	GString *str = NULL;

	g_return_val_if_fail(XEME_IS_STANZA(stanza), NULL);

	priv = xeme_stanza_get_instance_private(stanza);
	str = g_string_new("");

	/* Attributes should remain in alphabetical order for consistency. */

	if(!xeme_str_is_empty(priv->from)) {
		g_string_append_printf(str, " from=\"%s\"", priv->from);
	}

	if(!xeme_str_is_empty(priv->id)) {
		g_string_append_printf(str, " id=\"%s\"", priv->id);
	}

	if(!xeme_str_is_empty(priv->to)) {
		g_string_append_printf(str, " to=\"%s\"", priv->to);
	}

	if(!xeme_str_is_empty(priv->content_type)) {
		g_string_append_printf(str, " type=\"%s\"", priv->content_type);
	}

	if(!xeme_str_is_empty(priv->xml_lang)) {
		g_string_append_printf(str, " xml:lang=\"%s\"", priv->xml_lang);
	}

	/* Finally remove the leading space that we added to make appending
	 * everything easier.
	 */
	if(str->len > 0) {
		g_string_erase(str, 0, 1);
	}

	return g_string_free_and_steal(str);
}
07070100000035000081A400000000000000000000000167AEF511000012D9000000000000000000000000000000000000001C00000000xeme-0+53/xeme/xemestanza.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_STANZA_H
#define XEME_STANZA_H

#include <glib.h>
#include <glib-object.h>

#include "xemeversion.h"

G_BEGIN_DECLS

#define XEME_TYPE_STANZA (xeme_stanza_get_type())

/**
 * XemeStanza:
 *
 * The base class for XMPP stanzas including Message, IQ, and Presence.
 *
 * Since: 0.1
 */

XEME_AVAILABLE_IN_0_1
G_DECLARE_DERIVABLE_TYPE(XemeStanza, xeme_stanza, XEME, STANZA, GObject)

struct _XemeStanzaClass {
	/*< private >*/
	GObjectClass parent;

	/*< public >*/
	char *(*marshal)(XemeStanza *stanza, GError **error);

	/*< private >*/
	gpointer reserved[8];
};

/**
 * xeme_stanza_get_content_type:
 * @stanza: The instance.
 *
 * Gets the content type of @stanza.
 *
 * Returns: (nullable): The content type of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stanza_get_content_type(XemeStanza *stanza);

/**
 * xeme_stanza_set_content_type:
 * @stanza: The instance.
 * @content_type: (not nullable): The new content type for the stanza.
 *
 * Sets the content type of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stanza_set_content_type(XemeStanza *stanza, const char *content_type);

/**
 * xeme_stanza_get_from:
 * @stanza: The instance.
 *
 * Gets the sender of @stanza.
 *
 * Returns: (nullable): The sender of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stanza_get_from(XemeStanza *stanza);

/**
 * xeme_stanza_set_from:
 * @stanza: The instance.
 * @from: (not nullable): Who sent this stanza.
 *
 * Sets the sender of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stanza_set_from(XemeStanza *stanza, const char *from);

/**
 * xeme_stanza_get_id:
 * @stanza: The instance.
 *
 * Gets the id of @stanza.
 *
 * Returns: (nullable): The identifier of the stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stanza_get_id(XemeStanza *stanza);

/**
 * xeme_stanza_set_id:
 * @stanza: The instance.
 * @id: (not nullable): The new identifier.
 *
 * Sets the identifier of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stanza_set_id(XemeStanza *stanza, const char *id);

/**
 * xeme_stanza_get_to:
 * @stanza: The instance.
 *
 * Gets the recipient of @stanza.
 *
 * Returns: (nullable): The recipient of this stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stanza_get_to(XemeStanza *stanza);

/**
 * xeme_stanza_set_to:
 * @stanza: The instance.
 * @to: (not nullable): Who this stanza is addressed to.
 *
 * Sets the recipient of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stanza_set_to(XemeStanza *stanza, const char *to);

/**
 * xeme_stanza_get_xml_lang:
 * @stanza: The instance.
 *
 * Gets the XML Lang of @stanza.
 *
 * Returns: (nullable): The XML lang of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stanza_get_xml_lang(XemeStanza *stanza);

/**
 * xeme_stanza_set_xml_lang:
 * @stanza: The instance.
 * @xml_lang: (nullable): The new XML Lang.
 *
 * Sets the XML Lang of @stanza.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stanza_set_xml_lang(XemeStanza *stanza, const char *xml_lang);

/**
 * xeme_stanza_marshal:
 * @stanza: The instance.
 * @error: (nullable): A return address for a #GError.
 *
 * Marshals @stanza into a string. If there is an error @error will be set and
 * %NULL will be returned.
 *
 * Returns: (transfer full) (nullable): The marshaled stanza as a string or
 *          %NULL with error set on failure.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
char *xeme_stanza_marshal(XemeStanza *stanza, GError **error);

/**
 * xeme_stanza_marshal_attributes:
 * @stanza: The instance.
 *
 * Marshals the common attributes: id, to, from, type, and xml:lang to a
 * string.
 *
 * If none of the attributes are set, %NULL will be returned. If at least one
 * attribute is set, the returned string will not have any padding so you will
 * have to add your own.
 *
 * Returns: (transfer full) (nullable): The attributes marshaled as a string.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
char *xeme_stanza_marshal_attributes(XemeStanza *stanza);

G_END_DECLS

#endif /* XEME_STANZA_H */
07070100000036000081A400000000000000000000000167AEF511000022E4000000000000000000000000000000000000001C00000000xeme-0+53/xeme/xemestream.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemecore.h"
#include "xemestream.h"
#include "xemestring.h"

enum {
	PROP_0,
	PROP_CANCELLABLE,
	PROP_ID,
	PROP_TO,
	PROP_FROM,
	PROP_LANGUAGE,
	PROP_VERSION,
	N_PROPERTIES,
};
static GParamSpec *properties[N_PROPERTIES] = {NULL, };

typedef struct {
	GCancellable *cancellable;

	char *id;
	char *to;
	char *from;
	char *language;
	char *version;
} XemeStreamPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(XemeStream, xeme_stream, G_TYPE_OBJECT)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void
xeme_stream_set_cancellable(XemeStream *stream, GCancellable *cancellable) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);

	if(g_set_object(&priv->cancellable, cancellable)) {
		g_object_notify_by_pspec(G_OBJECT(stream),
		                         properties[PROP_CANCELLABLE]);
	}
}

/******************************************************************************
 * GObject Implementation
 *****************************************************************************/
static void
xeme_stream_finalize(GObject *obj) {
	XemeStream *stream = XEME_STREAM(obj);
	XemeStreamPrivate *priv = NULL;

	priv = xeme_stream_get_instance_private(stream);

	g_clear_object(&priv->cancellable);
	g_clear_pointer(&priv->id, g_free);
	g_clear_pointer(&priv->to, g_free);
	g_clear_pointer(&priv->from, g_free);
	g_clear_pointer(&priv->language, g_free);
	g_clear_pointer(&priv->version, g_free);

	G_OBJECT_CLASS(xeme_stream_parent_class)->finalize(obj);
}

static void
xeme_stream_get_property(GObject *obj, guint param_id, GValue *value,
                         GParamSpec *pspec)
{
	XemeStream *stream = XEME_STREAM(obj);

	switch(param_id) {
	case PROP_CANCELLABLE:
		g_value_set_object(value, xeme_stream_get_cancellable(stream));
		break;
	case PROP_ID:
		g_value_set_string(value, xeme_stream_get_id(stream));
		break;
	case PROP_TO:
		g_value_set_string(value, xeme_stream_get_to(stream));
		break;
	case PROP_FROM:
		g_value_set_string(value, xeme_stream_get_from(stream));
		break;
	case PROP_LANGUAGE:
		g_value_set_string(value, xeme_stream_get_language(stream));
		break;
	case PROP_VERSION:
		g_value_set_string(value, xeme_stream_get_version(stream));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_stream_set_property(GObject *obj, guint param_id, const GValue *value,
                         GParamSpec *pspec)
{
	XemeStream *stream = XEME_STREAM(obj);

	switch(param_id) {
	case PROP_CANCELLABLE:
		xeme_stream_set_cancellable(stream, g_value_get_object(value));
		break;
	case PROP_ID:
		xeme_stream_set_id(stream, g_value_get_string(value));
		break;
	case PROP_TO:
		xeme_stream_set_to(stream, g_value_get_string(value));
		break;
	case PROP_FROM:
		xeme_stream_set_from(stream, g_value_get_string(value));
		break;
	case PROP_LANGUAGE:
		xeme_stream_set_language(stream, g_value_get_string(value));
		break;
	case PROP_VERSION:
		xeme_stream_set_version(stream, g_value_get_string(value));
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
		break;
	}
}

static void
xeme_stream_init(XemeStream *stream) {
	/* Set our defaults. */
	xeme_stream_set_version(stream, XEME_STREAM_VERSION);
}

static void
xeme_stream_class_init(XemeStreamClass *klass) {
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	obj_class->finalize = xeme_stream_finalize;
	obj_class->get_property = xeme_stream_get_property;
	obj_class->set_property = xeme_stream_set_property;

	/**
	 * XemeStream:cancellable:
	 *
	 * The cancellable for this stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_CANCELLABLE] = g_param_spec_object(
		"cancellable", NULL, NULL,
		G_TYPE_CANCELLABLE,
		G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStream:id:
	 *
	 * The identifier of the stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_ID] = g_param_spec_string(
		"id", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStream:to:
	 *
	 * The bare jid of the remote side of this stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_TO] = g_param_spec_string(
		"to", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStream:from:
	 *
	 * The bare jid of the local side of this stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_FROM] = g_param_spec_string(
		"from", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStream:language:
	 *
	 * The language for this stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_LANGUAGE] = g_param_spec_string(
		"language", NULL, NULL,
		NULL,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	/**
	 * XemeStream:version:
	 *
	 * The XMPP version for this stream.
	 *
	 * Since: 0.1
	 */
	properties[PROP_VERSION] = g_param_spec_string(
		"version", NULL, NULL,
		XEME_STREAM_VERSION,
		G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
}

/******************************************************************************
 * Public API
 *****************************************************************************/
GCancellable *
xeme_stream_get_cancellable(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->cancellable;
}

const char *
xeme_stream_get_id(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->id;
}

void
xeme_stream_set_id(XemeStream *stream, const char *id) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);

	if(g_set_str(&priv->id, id)) {
		g_object_notify_by_pspec(G_OBJECT(stream), properties[PROP_ID]);
	}
}

const char *
xeme_stream_get_to(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->to;
}

void
xeme_stream_set_to(XemeStream *stream, const char *to) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);

	if(g_set_str(&priv->to, to)) {
		g_object_notify_by_pspec(G_OBJECT(stream), properties[PROP_TO]);
	}
}

const char *
xeme_stream_get_from(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->from;
}

void
xeme_stream_set_from(XemeStream *stream, const char *from) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);

	if(g_set_str(&priv->from, from)) {
		g_object_notify_by_pspec(G_OBJECT(stream), properties[PROP_FROM]);
	}
}

const char *
xeme_stream_get_language(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->language;
}

void
xeme_stream_set_language(XemeStream *stream, const char *language) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);

	if(g_set_str(&priv->language, language)) {
		g_object_notify_by_pspec(G_OBJECT(stream),
		                         properties[PROP_LANGUAGE]);
	}
}

const char *
xeme_stream_get_version(XemeStream *stream) {
	XemeStreamPrivate *priv = NULL;

	g_return_val_if_fail(XEME_IS_STREAM(stream), NULL);

	priv = xeme_stream_get_instance_private(stream);

	return priv->version;
}

void
xeme_stream_set_version(XemeStream *stream, const char *version) {
	XemeStreamPrivate *priv = NULL;

	g_return_if_fail(XEME_IS_STREAM(stream));

	priv = xeme_stream_get_instance_private(stream);
	if(g_set_str(&priv->version, version)) {
		g_object_notify_by_pspec(G_OBJECT(stream), properties[PROP_VERSION]);
	}
}
07070100000037000081A400000000000000000000000167AEF5110000136D000000000000000000000000000000000000001C00000000xeme-0+53/xeme/xemestream.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_STREAM_H
#define XEME_STREAM_H

#include <glib.h>
#include <glib-object.h>

#include <gio/gio.h>

#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * XEME_STREAM_PI:
 *
 * A constant for the XML processing instruction with a UTF-8 encoding.
 *
 * Since: 0.1
 */
#define XEME_STREAM_PI "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_STREAM_VERSION:
 *
 * The version of the XMPP stream protocol.
 *
 * Since: 0.1
 */
#define XEME_STREAM_VERSION "1.0" XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_STREAM_XMLNS:
 *
 * The XML namespace that is used as the default namespace for the stream.
 *
 * Since: 0.1
 */
#define XEME_STREAM_XMLNS "jabber:client" XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XEME_STREAM_XMLNS_STREAM:
 *
 * The XML namespace that is used for the `streams` namespace.
 *
 * Since: 0.1
 */
#define XEME_STREAM_XMLNS_STREAM "http://etherx.jabber.org/streams" XEME_AVAILABLE_MACRO_IN_0_1

/**
 * XemeStream:
 *
 * A base class for all streams.
 *
 * Since: 0.1
 */

#define XEME_TYPE_STREAM (xeme_stream_get_type())

XEME_AVAILABLE_IN_0_1
G_DECLARE_DERIVABLE_TYPE(XemeStream, xeme_stream, XEME, STREAM, GObject)

struct _XemeStreamClass {
	/*< private >*/
	GObjectClass parent;

	/*< private >*/
	gpointer reserved[8];
};

/**
 * xeme_stream_get_cancellable:
 * @stream: The instance.
 *
 * Gets the cancellable that @stream is using.
 *
 * Returns: (transfer none): The cancellable for @stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
GCancellable *xeme_stream_get_cancellable(XemeStream *stream);

/**
 * xeme_stream_get_id:
 * @stream: The instance.
 *
 * Gets the identifier for @stream.
 *
 * Returns: The id.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stream_get_id(XemeStream *stream);

/**
 * xeme_stream_set_id:
 * @stream: The instance.
 * @id: (nullable): The new id.
 *
 * Sets the identifier for @stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stream_set_id(XemeStream *stream, const char *id);

/**
 * xeme_stream_get_to:
 * @stream: The instance.
 *
 * Gets the bare jid of the remote side of @stream.
 *
 * Returns: The bare jid of the remote side.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stream_get_to(XemeStream *stream);

/**
 * xeme_stream_set_to:
 * @stream: The instance.
 * @to: (nullable): The bare jid of the remote side.
 *
 * Sets the bare jid for the remote side of @stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stream_set_to(XemeStream *stream, const char *to);

/**
 * xeme_stream_get_from:
 * @stream: The instance.
 *
 * Gets the bare jid of the local side of @stream.
 *
 * Returns: The bare jid of the local side.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stream_get_from(XemeStream *stream);

/**
 * xeme_stream_set_from:
 * @stream: The instance.
 * @from: (nullable): The bare jid of the local side.
 *
 * Sets the bare jid for the local side of @stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stream_set_from(XemeStream *stream, const char *from);

/**
 * xeme_stream_get_language:
 * @stream: The instance.
 *
 * Gets the language that @stream is using.
 *
 * Returns: The language that @stream is using.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stream_get_language(XemeStream *stream);

/**
 * xeme_stream_set_language:
 * @stream: The instance.
 * @language: (nullable): The language for this stream.
 *
 * Sets the language of @stream to @language. This is only really useful
 * before the stream is started but will be populated if the remote side
 * sends it.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stream_set_language(XemeStream *stream, const char *language);

/**
 * xeme_stream_get_version:
 * @stream: The instance.
 *
 * Gets the XMPP version for the stream.
 *
 * Returns: The version of @stream or %NULL if unset.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
const char *xeme_stream_get_version(XemeStream *stream);

/**
 * xeme_stream_set_version:
 * @stream: The instance.
 * @version: The version to set for the stream.
 *
 * Sets the XMPP version for @stream.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_0_1
void xeme_stream_set_version(XemeStream *stream, const char *version);

G_END_DECLS

#endif /* XEME_STREAM_H */
07070100000038000081A400000000000000000000000167AEF51100000620000000000000000000000000000000000000001C00000000xeme-0+53/xeme/xemestring.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_STRING_H
#define XEME_STRING_H

#include <glib.h>

#include "xemeversion.h"

G_BEGIN_DECLS

/**
 * xeme_str_is_empty:
 * @str: (nullable): The string to check.
 *
 * Determines if @str is empty or not.
 *
 * Returns: %TRUE if @str is empty, otherwise %FALSE.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_STATIC_INLINE_IN_0_1
static inline gboolean
xeme_str_is_empty(const char *str) {
	return (str == NULL || str[0] == '\0');
}

/**
 * xeme_str_equal:
 * @str1: (nullable): The first string.
 * @str2: (nullable): The second string.
 *
 * Checks if @str1 is equal to @str2.
 *
 * Returns: %TRUE if the strings are equal or %FALSE.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_STATIC_INLINE_IN_0_1
static inline gboolean
xeme_str_equal(const char *str1, const char *str2) {
	return (g_strcmp0(str1, str2) == 0);
}

G_END_DECLS


#endif /* XEME_STRING_H */
07070100000039000081A400000000000000000000000167AEF5110000076F000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xemeversion.c/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#include "xemeversion.h"

/******************************************************************************
 * Externals
 *****************************************************************************/
const guint xeme_major_version = XEME_MAJOR_VERSION;
const guint xeme_minor_version = XEME_MINOR_VERSION;
const guint xeme_micro_version = XEME_MICRO_VERSION;
const char *xeme_version = XEME_VERSION;

/******************************************************************************
 * Public API
 *****************************************************************************/
const char *
xeme_check_version(guint required_major, guint required_minor,
                   guint required_micro)
{
	if(required_major > XEME_MAJOR_VERSION) {
		return "xeme version too old (major mismatch)";
	}

#if XEME_CHECK_VERSION(1, 0, 0)
	if(required_major < XEME_MAJOR_VERSION) {
		return "xeme version too new (major mismatch)";
	}
#endif

	if(required_minor > XEME_MINOR_VERSION) {
		return "xeme version too old (minor mismatch)";
	}

	if((required_minor == XEME_MINOR_VERSION) &&
	   (required_micro > XEME_MICRO_VERSION))
	{
		return "xeme version too old (micro mismatch)";
	}

	return NULL;
}
0707010000003A000081A400000000000000000000000167AEF51100001AEA000000000000000000000000000000000000001D00000000xeme-0+53/xeme/xemeversion.h/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_VERSION_H
#define XEME_VERSION_H

#include <glib.h>

#include "xemeversionconsts.h"

/* clang-format tries to remove the space after 'if', which confuses
 * gobject-introspection, so turn it off temporarily. */
/* clang-format off */
#if (defined(_WIN32) || defined(__CYGWIN__)) && \
	!defined(XEME_STATIC_COMPILATION)
/* clang-format on */
#define _XEME_EXPORT __declspec(dllexport)
#define _XEME_IMPORT __declspec(dllimport)
#elif __GNUC__ >= 4
#define _XEME_EXPORT __attribute__((visibility("default")))
#define _XEME_IMPORT
#else
#define _XEME_EXPORT
#define _XEME_IMPORT
#endif
#ifdef XEME_COMPILATION
#define _XEME_API _XEME_EXPORT
#else
#define _XEME_API _XEME_IMPORT
#endif

#define _XEME_EXTERN _XEME_API extern

#ifdef XEME_DISABLE_DEPRECATION_WARNINGS
#define XEME_DEPRECATED _XEME_EXTERN
#define XEME_DEPRECATED_FOR(f) _XEME_EXTERN
#define XEME_UNAVAILABLE(maj, min) _XEME_EXTERN
#define XEME_UNAVAILABLE_STATIC_INLINE(maj, min)
#define XEME_UNAVAILABLE_TYPE(maj, min)
#else
#define XEME_DEPRECATED G_DEPRECATED _XEME_EXTERN
#define XEME_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _XEME_EXTERN
#define XEME_UNAVAILABLE(maj, min) G_UNAVAILABLE(maj, min) _XEME_EXTERN
#define XEME_UNAVAILABLE_STATIC_INLINE(maj, min) G_UNAVAILABLE(maj, min)
#define XEME_UNAVAILABLE_TYPE(maj, min) G_UNAVAILABLE(maj, min)
#endif

/**
 * XEME_VERSION_MIN_REQUIRED:
 *
 * A macro that should be defined by the user prior to including the `xeme.h`
 * header.
 *
 * The definition should be one of the predefined Xeme version macros:
 * %XEME_VERSION_0_1, %XEME_VERSION_0_2, ...
 *
 * This macro defines the earliest version of Xeme that the package is required
 * to be able to compile against.
 *
 * If the compiler is configured to warn about the use of deprecated functions,
 * then using functions that were deprecated in version
 * %XEME_VERSION_MIN_REQUIRED or earlier will cause warnings (but using
 * functions deprecated in later releases will not).
 *
 * Since: 0.1
 */

/**
 * XEME_VERSION_CUR_STABLE:
 *
 * A macro that evaluates to the current stable version of xeme, in a format
 * that can be used by the C pre-processor.
 *
 * Since: 0.1
 */
#define XEME_VERSION_CUR_STABLE \
	(G_ENCODE_VERSION(XEME_MAJOR_VERSION, XEME_MINOR_VERSION))

#ifndef XEME_VERSION_MIN_REQUIRED
#define XEME_VERSION_MIN_REQUIRED (XEME_VERSION_CUR_STABLE)
#elif XEME_VERSION_MIN_REQUIRED == 0
#undef XEME_VERSION_MIN_REQUIRED
#define XEME_VERSION_MIN_REQUIRED (XEME_VERSION_CUR_STABLE + 1)
#endif /* XEME_VERSION_MIN_REQUIRED */

#if !defined(XEME_VERSION_MAX_ALLOWED) || (XEME_VERSION_MAX_ALLOWED == 0)
#undef XEME_VERSION_MAX_ALLOWED
#define XEME_VERSION_MAX_ALLOWED (XEME_VERSION_CUR_STABLE)
#endif /* XEME_VERSION_MAX_ALLOWED */

/* sanity checks */
#if XEME_VERSION_MIN_REQUIRED > XEME_VERSION_CUR_STABLE
#error "XEME_VERSION_MIN_REQUIRED must be <= XEME_VERSION_CUR_STABLE"
#endif
#if XEME_VERSION_MAX_ALLOWED < XEME_VERSION_MIN_REQUIRED
#error "XEME_VERSION_MAX_ALLOWED must be >= XEME_VERSION_MIN_REQUIRED"
#endif
#if XEME_VERSION_MIN_REQUIRED < G_ENCODE_VERSION(0, 1)
#error "XEME_VERSION_MIN_REQUIRED must be >= XEME_VERSION_0_1"
#endif

#define XEME_VAR _XEME_EXTERN
#define XEME_AVAILABLE_IN_ALL _XEME_EXTERN

/**
 * XEME_VERSION_0_1:
 *
 * A macro that evaluates to the 0.1 version of xeme, in a format that can be
 * used by the C pre-processor.
 *
 * Since: 0.1
 */
#define XEME_VERSION_0_1 (G_ENCODE_VERSION(0, 1))

#if XEME_VERSION_MAX_ALLOWED < XEME_VERSION_0_1
#define XEME_AVAILABLE_IN_0_1 XEME_UNAVAILABLE(0, 1)
#define XEME_AVAILABLE_STATIC_INLINE_IN_0_1 XEME_UNAVAILABLE_STATIC_INLINE(0, 1)
#define XEME_AVAILABLE_MACRO_IN_0_1 XEME_UNAVAILABLE_MACRO(0, 1)
#define XEME_AVAILABLE_ENUMERATOR_IN_0_1 XEME_UNAVAILABLE_ENUMERATOR(0, 1)
#define XEME_AVAILABLE_TYPE_IN_0_1 XEME_UNAVAILABLE_TYPE(0, 1)
#else
#define XEME_AVAILABLE_IN_0_1 _XEME_EXTERN
#define XEME_AVAILABLE_STATIC_INLINE_IN_0_1
#define XEME_AVAILABLE_MACRO_IN_0_1
#define XEME_AVAILABLE_ENUMERATOR_IN_0_1
#define XEME_AVAILABLE_TYPE_IN_0_1
#endif

G_BEGIN_DECLS

/**
 * XEME_CHECK_VERSION:
 * @major: The major version to check for.
 * @minor: The minor version to check for.
 * @micro: The micro version to check for.
 *
 * Checks the version of xeme being compiled against. See [func@check_version]
 * for a runtime check.
 *
 * Returns: %TRUE if the version is the same or newer than the passed-in
 *          version.
 *
 * Since: 0.1
 */
#define XEME_CHECK_VERSION(major, minor, micro) ((major) == XEME_MAJOR_VERSION && \
                                                 ((minor) < XEME_MINOR_VERSION || \
                                                  ((minor) == XEME_MINOR_VERSION && (micro) <= XEME_MICRO_VERSION)))

/**
 * xeme_check_version:
 * @required_major: the required major version.
 * @required_minor: the required minor version.
 * @required_micro: the required micro version.
 *
 * Checks that the xeme version is compatible with the requested version.
 *
 * Returns: %NULL if the versions are compatible, or a string describing
 *          the version mismatch if not compatible.
 *
 * Since: 0.1
 */
XEME_AVAILABLE_IN_ALL
const char *xeme_check_version(guint required_major, guint required_minor, guint required_micro);

/**
 * xeme_version:
 *
 * The full version string of the running xeme.
 *
 * Since: 0.1
 */
XEME_VAR const char *xeme_version;

/**
 * xeme_major_version:
 *
 * The major version of the running xeme. Contrast with #XEME_MAJOR_VERSION,
 * which expands at compile time to the major version of xeme being compiled
 * against.
 *
 * Since: 0.1
 */
XEME_VAR const guint xeme_major_version;

/**
 * xeme_minor_version:
 *
 * The minor version of the running xeme. Contrast with #XEME_MINOR_VERSION,
 * which expands at compile time to the minor version of xeme being compiled
 * against.
 *
 * Since: 0.1
 */
XEME_VAR const guint xeme_minor_version;

/**
 * xeme_micro_version:
 *
 * The micro version of the running xeme. Contrast with #XEME_MICRO_VERSION,
 * which expands at compile time to the micro version of xeme being compiled
 * against.
 *
 * Since: 0.1
 */
XEME_VAR const guint xeme_micro_version;

G_END_DECLS

#endif /* XEME_VERSION_CONSTS_H */
0707010000003B000081A400000000000000000000000167AEF51100000688000000000000000000000000000000000000002600000000xeme-0+53/xeme/xemeversionconsts.h.in/*
 * Copyright (C) 2023 Xeme Developers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#ifndef XEME_VERSION_CONSTS_H
#define XEME_VERSION_CONSTS_H

/**
 * XEME_MAJOR_VERSION:
 *
 * The major version of the version of XEME that was compiled against.
 *
 * Since: 0.1
 */
#define XEME_MAJOR_VERSION (@XEME_MAJOR_VERSION@)

/**
 * XEME_MINOR_VERSION:
 *
 * The minor version of the version of XEME that was compiled against.
 *
 * Since: 0.1
 */
#define XEME_MINOR_VERSION (@XEME_MINOR_VERSION@)

/**
 * XEME_MICRO_VERSION:
 *
 * The micro version of the version of XEME that was compiled against.
 *
 * Since: 0.1
 */
#define XEME_MICRO_VERSION (@XEME_MICRO_VERSION@)

/**
 * XEME_EXTRA_VERSION:
 *
 * The extra version of the version of XEME that was compiled against.
 *
 * Since: 0.1
 */
#define XEME_EXTRA_VERSION ("@XEME_EXTRA_VERSION@")

/**
 * XEME_VERSION:
 *
 * The full version number for the version of XEME that was compiled against.
 *
 * Since: 0.1
 */
#define XEME_VERSION ("@XEME_VERSION@")

#endif /* XEME_VERSION_CONSTS_H */
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!442 blocks
openSUSE Build Service is sponsored by