Compare commits
3 Commits
fix/functi
...
b002e6237b
| Author | SHA1 | Date | |
|---|---|---|---|
| b002e6237b | |||
| 30504faf54 | |||
| 6e84db20d4 |
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
44
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,44 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
<!--- More than 50 issues open? Please don't file any new feature requests -->
|
||||
<!--- Help us reduce the work first :-) -->
|
||||
|
||||
## Expected Behavior
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
<!--- If you're suggesting a change/improvement, tell us how it should work -->
|
||||
|
||||
## Current Behavior
|
||||
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
|
||||
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
|
||||
<!--- or ideas how to implement the addition or change -->
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Describe, in detail, what needs to happen to reproduce this bug -->
|
||||
<!--- Give us a screenshot (if it's helpful for this particular bug) -->
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Context
|
||||
<!--- How has this issue affected you? What are you trying to accomplish? -->
|
||||
|
||||
## Environment
|
||||
* Give us the version and build information output generated by `profanity -v`
|
||||
* If you could not yet build profanity, mention the revision you try to build from
|
||||
* Operating System/Distribution
|
||||
* glib version
|
||||
* libstrophe version
|
||||
* Some bugs might be due to specific implementation in the server. `/serversoftware example.domain` can be helpful
|
||||
44
.github/workflows/ci-api-docs.yml
vendored
44
.github/workflows/ci-api-docs.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: CI API Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'apidocs/**'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- 'apidocs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test-c-api-docs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test C API Documentation Generation
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends make doxygen
|
||||
- name: Test C API docs generation
|
||||
run: |
|
||||
cd apidocs/c/
|
||||
doxygen c-prof.conf
|
||||
|
||||
test-python-api-docs:
|
||||
runs-on: ubuntu-latest
|
||||
name: Test Python API Documentation Generation
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends make python3-sphinx
|
||||
- name: Test Python API docs generation
|
||||
run: |
|
||||
cd apidocs/python/
|
||||
sphinx-apidoc -f -o . src && make -j$(nproc) xml SPHINXOPTS="-W --keep-going -n"
|
||||
@@ -1,18 +1,10 @@
|
||||
name: CI Code
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'apidocs/**'
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'apidocs/**'
|
||||
- 'README.md'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -24,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
flavor: [arch, debian, ubuntu]
|
||||
flavor: [arch, debian, fedora, ubuntu]
|
||||
|
||||
name: Linux
|
||||
steps:
|
||||
@@ -91,7 +83,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install dependencies
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y --no-install-recommends codespell
|
||||
@@ -137,38 +137,3 @@ You can run the `make spell` command for this.
|
||||
|
||||
`make doublecheck` will run the code formatter, spell checker and unit tests.
|
||||
|
||||
|
||||
### Functional tests: moving away from brittle ID hooks
|
||||
|
||||
Historically the functional test suite relied on stabber's id based helpers like `stbbr_for_id("prof_presence_1", ...)` to register canned responses that would be sent once Profanity emitted a stanza carrying that exact `id` attribute. This made the tests fragile:
|
||||
|
||||
* Changes to stanza id generation (sequence, format) broke tests unexpectedly.
|
||||
* Reordering internal requests produced hard-to-debug race conditions when an `id` no longer matched.
|
||||
* Parallel additions of new features could shift which stanzas received a given id causing unrelated test failures.
|
||||
|
||||
We have migrated to content based stubbing using direct sends (`stbbr_send`) and query hooks (`stbbr_for_query`). Instead of tying a response to a predicted id we now send the required server stanzas explicitly after initiating actions. Example (see `tests/functionaltests/proftest.c`):
|
||||
|
||||
```c
|
||||
// Old brittle approach
|
||||
stbbr_for_id("prof_presence_1", "<presence id='prof_presence_1' ...>");
|
||||
|
||||
// New approach: after authentication, send presence directly
|
||||
stbbr_send("<presence from='stabber@localhost/profanity' to='stabber@localhost/profanity'>...caps...</presence>");
|
||||
```
|
||||
|
||||
Benefits:
|
||||
|
||||
* Eliminates dependency on internal id sequencing.
|
||||
* Clearer intent inside test code ("send presence now" vs "register hook and hope client triggers it").
|
||||
* Simplifies adding new tests—no need to inspect logs for generated ids.
|
||||
|
||||
Guidelines when writing new functional tests:
|
||||
|
||||
1. Prefer `stbbr_for_query(namespace, xml)` for IQ roster or disco queries where the namespace is stable.
|
||||
2. Use `stbbr_send(xml)` for presence, message, and other push style stanzas.
|
||||
3. Avoid `stbbr_for_id` unless the protocol flow genuinely requires correlating a specific request/response pair not covered by a namespace query.
|
||||
4. Keep assertions tolerant of ordering when possible; rely on `prof_output_regex()` matches rather than hard-coded positions.
|
||||
5. If timing flakiness appears, temporarily raise `prof_timeout()` around the critical expectation and reset it immediately afterwards.
|
||||
|
||||
The migration from `stbbr_for_id` is complete. All functional tests now use content-based stubbing. When adding new tests, follow the guidelines above.
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
FROM archlinux
|
||||
|
||||
ENV TERM=xterm
|
||||
|
||||
RUN pacman -Syu --noconfirm
|
||||
# reflector is optional - if it fails due to network issues, continue with default mirrorlist
|
||||
RUN pacman -S --needed --noconfirm reflector && \
|
||||
(reflector --latest 20 --protocol https --sort rate --save /etc/pacman.d/mirrorlist || true)
|
||||
RUN pacman-key --init
|
||||
RUN pacman -S --needed --noconfirm \
|
||||
RUN pacman -Syu --noconfirm && pacman -S --needed --noconfirm \
|
||||
autoconf \
|
||||
autoconf-archive \
|
||||
automake \
|
||||
@@ -22,7 +15,7 @@ RUN pacman -S --needed --noconfirm \
|
||||
gcc \
|
||||
git \
|
||||
gpgme \
|
||||
gtk3 \
|
||||
gtk2 \
|
||||
libgcrypt \
|
||||
libmicrohttpd \
|
||||
libnotify \
|
||||
@@ -61,13 +54,13 @@ USER root
|
||||
RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.zst
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber
|
||||
#RUN git clone https://github.com/boothj5/stabber
|
||||
|
||||
WORKDIR /usr/src/stabber
|
||||
RUN ./bootstrap.sh
|
||||
RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
RUN make
|
||||
RUN make install
|
||||
#WORKDIR /usr/src/stabber
|
||||
#RUN ./bootstrap.sh
|
||||
#RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
#RUN make
|
||||
#RUN make install
|
||||
|
||||
WORKDIR /usr/src/profanity
|
||||
COPY . /usr/src/profanity
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
# Build the latest Debian testing image
|
||||
FROM debian:testing
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive"
|
||||
ENV TERM=xterm
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
autoconf-archive \
|
||||
automake \
|
||||
expect \
|
||||
gcc \
|
||||
git \
|
||||
libcmocka-dev \
|
||||
@@ -37,14 +35,14 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
|
||||
WORKDIR /usr/src
|
||||
|
||||
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber
|
||||
#RUN git clone https://github.com/boothj5/stabber
|
||||
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe
|
||||
|
||||
WORKDIR /usr/src/stabber
|
||||
RUN ./bootstrap.sh
|
||||
RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
RUN make
|
||||
RUN make install
|
||||
#WORKDIR /usr/src/stabber
|
||||
#RUN ./bootstrap.sh
|
||||
#RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
#RUN make
|
||||
#RUN make install
|
||||
|
||||
WORKDIR /usr/src/libstrophe
|
||||
RUN ./bootstrap.sh
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Build the latest Fedora image
|
||||
FROM fedora:latest
|
||||
|
||||
# expect - for functional tests
|
||||
# libmicrohttpd - for stabber
|
||||
# glibc-locale - to have en_US locale
|
||||
RUN dnf install -y \
|
||||
@@ -8,6 +9,7 @@ RUN dnf install -y \
|
||||
autoconf-archive \
|
||||
automake \
|
||||
awk \
|
||||
expect-devel \
|
||||
gcc \
|
||||
git \
|
||||
glib2-devel \
|
||||
@@ -40,17 +42,17 @@ RUN dnf install -y \
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV TERM=xterm
|
||||
|
||||
RUN mkdir -p /usr/src
|
||||
WORKDIR /usr/src
|
||||
|
||||
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber
|
||||
WORKDIR /usr/src/stabber
|
||||
RUN ./bootstrap.sh
|
||||
RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
RUN make
|
||||
RUN make install
|
||||
#RUN mkdir -p /usr/src/stabber
|
||||
#RUN git clone https://github.com/boothj5/stabber
|
||||
#WORKDIR /usr/src/stabber
|
||||
#RUN ./bootstrap.sh
|
||||
#RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
#RUN make
|
||||
#RUN make install
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN mkdir -p /usr/src/libstrophe
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# Build the latest openSUSE Tumbleweed image
|
||||
FROM opensuse/tumbleweed
|
||||
|
||||
# expect - for functional tests
|
||||
# libmicrohttpd - for stabber
|
||||
# glibc-locale - to have en_US locale
|
||||
RUN zypper --non-interactive in --no-recommends \
|
||||
autoconf \
|
||||
autoconf-archive \
|
||||
automake \
|
||||
expect-devel \
|
||||
gcc \
|
||||
git \
|
||||
glib2-devel \
|
||||
@@ -39,17 +41,17 @@ RUN zypper --non-interactive in --no-recommends \
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV TERM=xterm
|
||||
|
||||
RUN mkdir -p /usr/src
|
||||
WORKDIR /usr/src
|
||||
|
||||
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber
|
||||
WORKDIR /usr/src/stabber
|
||||
RUN ./bootstrap.sh
|
||||
RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
RUN make
|
||||
RUN make install
|
||||
#RUN mkdir -p /usr/src/stabber
|
||||
#RUN git clone git://github.com/boothj5/stabber.git
|
||||
#WORKDIR /usr/src/stabber
|
||||
#RUN ./bootstrap.sh
|
||||
#RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
#RUN make
|
||||
#RUN make install
|
||||
|
||||
RUN mkdir -p /usr/src/profanity
|
||||
WORKDIR /usr/src/profanity
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive"
|
||||
ENV TERM=xterm
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
autoconf \
|
||||
autoconf-archive \
|
||||
automake \
|
||||
expect \
|
||||
gcc \
|
||||
git \
|
||||
libcmocka-dev \
|
||||
@@ -36,14 +36,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
|
||||
WORKDIR /usr/src
|
||||
|
||||
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber
|
||||
#RUN git clone https://github.com/boothj5/stabber
|
||||
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe
|
||||
|
||||
WORKDIR /usr/src/stabber
|
||||
RUN ./bootstrap.sh
|
||||
RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
RUN make
|
||||
RUN make install
|
||||
# TODO: Re-enable once libmicrohttpd-dev has been updated.
|
||||
#WORKDIR /usr/src/stabber
|
||||
#RUN ./bootstrap.sh
|
||||
#RUN ./configure --prefix=/usr --disable-dependency-tracking
|
||||
#RUN make
|
||||
#RUN make install
|
||||
|
||||
WORKDIR /usr/src/libstrophe
|
||||
RUN ./bootstrap.sh
|
||||
|
||||
38
Makefile.am
38
Makefile.am
@@ -124,7 +124,6 @@ unittest_sources = \
|
||||
src/event/server_events.c src/event/server_events.h \
|
||||
src/event/client_events.c src/event/client_events.h \
|
||||
src/ui/tray.h src/ui/tray.c \
|
||||
tests/prof_cmocka.h \
|
||||
tests/unittests/xmpp/stub_vcard.c \
|
||||
tests/unittests/xmpp/stub_avatar.c \
|
||||
tests/unittests/xmpp/stub_ox.c \
|
||||
@@ -167,11 +166,9 @@ unittest_sources = \
|
||||
tests/unittests/test_cmd_disconnect.c tests/unittests/test_cmd_disconnect.h \
|
||||
tests/unittests/test_callbacks.c tests/unittests/test_callbacks.h \
|
||||
tests/unittests/test_plugins_disco.c tests/unittests/test_plugins_disco.h \
|
||||
tests/unittests/test_forced_encryption.c tests/unittests/test_forced_encryption.h \
|
||||
tests/unittests/unittests.c
|
||||
|
||||
functionaltest_sources = \
|
||||
tests/prof_cmocka.h \
|
||||
tests/functionaltests/proftest.c tests/functionaltests/proftest.h \
|
||||
tests/functionaltests/test_connect.c tests/functionaltests/test_connect.h \
|
||||
tests/functionaltests/test_ping.c tests/functionaltests/test_ping.h \
|
||||
@@ -286,27 +283,24 @@ endif
|
||||
|
||||
TESTS = tests/unittests/unittests
|
||||
check_PROGRAMS = tests/unittests/unittests
|
||||
tests_unittests_unittests_CPPFLAGS = -Itests/
|
||||
tests_unittests_unittests_SOURCES = $(unittest_sources)
|
||||
tests_unittests_unittests_LDADD = -lcmocka
|
||||
|
||||
# Functional tests require libstabber.
|
||||
# They are only built when libstabber is available.
|
||||
# See: https://github.com/profanity-im/profanity/pull/1010
|
||||
# https://github.com/profanity-im/stabber/issues/5
|
||||
# Functional test were commented out because of:
|
||||
# https://github.com/profanity-im/profanity/pull/1010
|
||||
# An issue was raised for stabber:
|
||||
# https://github.com/profanity-im/stabber/issues/5
|
||||
# Once this issue is resolved functional tests should be enabled again
|
||||
#
|
||||
# Note: We use forkpty() instead of libexpect for PTY handling.
|
||||
|
||||
if HAVE_STABBER
|
||||
if HAVE_FORKPTY
|
||||
TESTS += tests/functionaltests/functionaltests
|
||||
check_PROGRAMS += tests/functionaltests/functionaltests
|
||||
tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
|
||||
tests_functionaltests_functionaltests_CPPFLAGS = -Itests/
|
||||
tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS)
|
||||
tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber @FORKPTY_LIB@
|
||||
endif
|
||||
endif
|
||||
#if HAVE_STABBER
|
||||
#if HAVE_EXPECT
|
||||
#TESTS += tests/functionaltests/functionaltests
|
||||
#check_PROGRAMS += tests/functionaltests/functionaltests
|
||||
#tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
|
||||
#tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) -I/usr/include/tcl8.6 -I/usr/include/tcl8.5
|
||||
#tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber -lexpect
|
||||
#endif
|
||||
#endif
|
||||
|
||||
man1_MANS = $(man1_sources)
|
||||
|
||||
@@ -352,13 +346,9 @@ endif
|
||||
.PHONY: my-prof.supp
|
||||
my-prof.supp:
|
||||
@sed '/^# AUTO-GENERATED START/q' prof.supp > $@
|
||||
@printf "\n\n# glib\n" >> $@
|
||||
@cat /usr/share/glib-2.0/valgrind/glib.supp >> $@
|
||||
@printf "\n\n# Python\n" >> $@
|
||||
@wget -O- https://raw.githubusercontent.com/python/cpython/refs/tags/v`python3 --version | cut -d' ' -f2`/Misc/valgrind-python.supp >> $@
|
||||
@printf "\n\n# gtk\n" >> $@
|
||||
@test -z "@GTK_VERSION@" || wget -O- https://raw.githubusercontent.com/GNOME/gtk/refs/tags/@GTK_VERSION@/gtk.supp >> $@
|
||||
@printf "\n\n# more gtk\n" >> $@
|
||||
@test -z "@GTK_VERSION@" || cat /usr/share/gtk-3.0/valgrind/gtk.supp >> $@
|
||||
|
||||
check-unit: tests/unittests/unittests
|
||||
|
||||
40
README.md
40
README.md
@@ -1,36 +1,40 @@
|
||||
# CProof
|
||||

|
||||
# Profanity
|
||||
 [](https://github.com/profanity-im/profanity/graphs/contributors/) [](https://repology.org/project/profanity/versions)
|
||||
|
||||
CProof is a console based XMPP chat client based on [Profanity](https://profanity-im.github.io/).
|
||||
Profanity is a console based XMPP client inspired by [Irssi](http://www.irssi.org/).
|
||||
|
||||

|
||||

|
||||
|
||||
See the [Quick Start Guide](https://jabber.space/getting-started/quick-start/) for information on installing and using CProof.
|
||||
See the [User Guide](https://profanity-im.github.io/userguide.html) for information on installing and using Profanity.
|
||||
|
||||
## Project
|
||||
CProof enables you to communicate with privacy, freedom and comfort. Our open-source chat application delivers secure, end-to-end encrypted messaging (OTR, PGP, OMEMO, OX) built on the trusted [XMPP](https://xmpp.org/) protocol. With a decentralized design, you can connect directly or even host your own server, keeping your data in your hands. Whether you're chatting with friends or collaborating securely, CProof makes private communication simple, reliable, and truly yours.
|
||||
This project is about freedom, privacy and choice. We want to enable people to chat with one another in a safe way. Thus supporting encryption (OTR, PGP, OMEMO, OX) and being decentralized, meaning everyone can run their own server. We believe [XMPP](https://xmpp.org/) is a great proven protocol with an excellent community serving this purpose well.
|
||||
|
||||
|
||||
## Installation
|
||||
Check our [installation guide](https://jabber.space/getting-started/installation/) for detailed instructions.
|
||||
Our [user guide](https://profanity-im.github.io/userguide.html) contains an [install](https://profanity-im.github.io/guide/latest/install.html) section and a section for [building](https://profanity-im.github.io/guide/latest/build.html) from source yourself.
|
||||
|
||||
## How to contribute
|
||||
See our [Helping Out](https://jabber.space/contributing/helpout/) page for a concise summary of ways to help us.
|
||||
|
||||
Review the [Contributing Guide](CONTRIBUTING.md) and [Code Overview](https://jabber.space/contributing/code-overview/) pages for advanced technical details.
|
||||
We tried to sum things up on our [helpout](https://profanity-im.github.io/helpout.html) page.
|
||||
Additionally you can check out our [blog](https://profanity-im.github.io/blog/) where we have articles like:
|
||||
[How to get a backtrace](https://profanity-im.github.io/blog/post/how-to-get-a-backtrace/) and [Contributing a Patch via GitHub](https://profanity-im.github.io/blog/post/contributing-a-patch-via-github/).
|
||||
For more technical details check out our [CONTRIBUTING.md](CONTRIBUTING.md) file.
|
||||
|
||||
## Getting help
|
||||
Prior to asking questions, check our [User Guide](https://profanity-im.github.io/userguide.html), then check out the [FAQ](https://jabber.space/about/faq/).
|
||||
|
||||
If you are still having a problem then search the [issue tracker](https://git.jabber.space/devs/cproof/issues).
|
||||
|
||||
As a last resort, feel free to write us on [support@jabber.tech](mailto:support@jabber.tech) or create a new issue depending on what your problem is.
|
||||
To get help, first read our [User Guide](https://profanity-im.github.io/userguide.html) then check out the [FAQ](https://profanity-im.github.io/faq.html).
|
||||
If you are having a problem then first search the [issue tracker](https://github.com/profanity-im/profanity/issues).
|
||||
If you don't find anything there either come to our [MUC](xmpp:profanity@rooms.dismail.de?join) or create a new issue depending on what your problem is.
|
||||
|
||||
## Links
|
||||
|
||||
### Website
|
||||
Feel free to visit our website: [jabber.space](https://jabber.space/).
|
||||
<!-- TODO:
|
||||
URL: https://jabber.space/
|
||||
-->
|
||||
|
||||
You may also check the repository if you like, available on [git.jabber.space/devs/profanity](https://git.jabber.space/devs/profanity).
|
||||
Repo: https://git.jabber.space/devs/profanity
|
||||
|
||||
<!-- TODO: blog/docs link -->
|
||||
|
||||
### Plugins
|
||||
Plugins repository: https://git.jabber.space/devs/cproof-plugins
|
||||
Plugins repository: https://github.com/profanity-im/profanity-plugins
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,247 +1,220 @@
|
||||
/** @file profhooks.h
|
||||
* @brief C Plugin Hooks for CProof.
|
||||
*
|
||||
* This header defines optional callback hooks that CProof plugins can implement to
|
||||
* handle events such as startup, shutdown, message processing, and presence updates in
|
||||
* CProof, a console-based XMPP client. These hooks are called by CProof when the
|
||||
* corresponding events occur, allowing plugins to customize behavior. All hooks are
|
||||
* optional; plugins can define only the hooks they need, and CProof will automatically
|
||||
* invoke them when appropriate.
|
||||
*
|
||||
* To use these hooks, define the functions in your plugin with the signatures provided
|
||||
* in this header. For example, to handle the startup event:
|
||||
*
|
||||
* @code
|
||||
* #include "profhooks.h"
|
||||
* void prof_on_start(void) {
|
||||
* // Handle CProof startup
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* For additional plugin functionality, such as displaying messages or registering
|
||||
* commands, see the CProof API functions in profapi.h.
|
||||
*
|
||||
* @see profapi.h
|
||||
*/
|
||||
|
||||
/** @mainpage CProof Plugins API and Hooks
|
||||
* List of all API functions available to plugins: @ref profapi.h
|
||||
* List of all hooks which plugins may implement: @ref profhooks.h
|
||||
*/
|
||||
/** @file
|
||||
C Hooks.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called when a plugin is loaded, either when CProof is started, or when the /plugins load or /plugins install commands are called
|
||||
* @param version The version of CProof
|
||||
* @param status The package status of CProof, "development" or "release"
|
||||
* @param account_name Account name of the currently logged in account, or NULL if not logged in
|
||||
* @param fulljid The user's full Jabber ID (barejid and resource) if logged in, NULL otherwise
|
||||
*/
|
||||
Called when a plugin is loaded, either when profanity is started, or when the /plugins load or /plugins install commands are called
|
||||
@param version the version of Profanity
|
||||
@param status the package status of Profanity, "development" or "release"
|
||||
@param account_name account name of the currently logged in account, or NULL if not logged in
|
||||
@param fulljid the users full Jabber ID (barejid and resource) if logged in, NULL otherwise
|
||||
*/
|
||||
void prof_init(const char * const version, const char * const status, const char *const account_name, const char *const fulljid);
|
||||
|
||||
/**
|
||||
* Called when CProof is started
|
||||
*/
|
||||
Called when Profanity is started
|
||||
*/
|
||||
void prof_on_start(void);
|
||||
|
||||
/**
|
||||
* Called when the user quits CProof
|
||||
*/
|
||||
Called when the user quits Profanity
|
||||
*/
|
||||
void prof_on_shutdown(void);
|
||||
|
||||
/**
|
||||
* Called when a plugin is unloaded with the /plugins unload command
|
||||
*/
|
||||
Called when a plugin is unloaded with the /plugins unload command
|
||||
*/
|
||||
void prof_on_unload(void);
|
||||
|
||||
/**
|
||||
* Called when the user connects with an account
|
||||
* @param account_name Account name of the account used for logging in
|
||||
* @param fulljid The full Jabber ID (barejid and resource) of the account
|
||||
*/
|
||||
Called when the user connects with an account
|
||||
@param account_name account name of the account used for logging in
|
||||
@param fulljid the full Jabber ID (barejid and resource) of the account
|
||||
*/
|
||||
void prof_on_connect(const char * const account_name, const char * const fulljid);
|
||||
|
||||
/**
|
||||
* Called when the user disconnects an account
|
||||
* @param account_name Account name of the account being disconnected
|
||||
* @param fulljid The full Jabber ID (barejid and resource) of the account
|
||||
*/
|
||||
Called when the user disconnects an account
|
||||
@param account_name account name of the account being disconnected
|
||||
@param fulljid the full Jabber ID (barejid and resource) of the account
|
||||
*/
|
||||
void prof_on_disconnect(const char * const account_name, const char * const fulljid);
|
||||
|
||||
/**
|
||||
* Called before a chat message is displayed
|
||||
* @param barejid Jabber ID of the message sender
|
||||
* @param resource Resource of the message sender
|
||||
* @param message The received message
|
||||
* @return The new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
Called before a chat message is displayed
|
||||
@param barejid Jabber ID of the message sender
|
||||
@param resource resource of the message sender
|
||||
@param message the received message
|
||||
@return the new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
char* prof_pre_chat_message_display(const char * const barejid, const char *const resource, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a chat message is displayed
|
||||
* @param barejid Jabber ID of the message sender
|
||||
* @param resource Resource of the message sender
|
||||
* @param message The received message
|
||||
*/
|
||||
Called after a chat message is displayed
|
||||
@param barejid Jabber ID of the message sender
|
||||
@param resource resource of the message sender
|
||||
@param message the received message
|
||||
*/
|
||||
void prof_post_chat_message_display(const char * const barejid, const char *const resource, const char *message);
|
||||
|
||||
/**
|
||||
* Called before a chat message is sent
|
||||
* @param barejid Jabber ID of the message recipient
|
||||
* @param message The message to be sent
|
||||
* @return The modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
Called before a chat message is sent
|
||||
@param barejid Jabber ID of the message recipient
|
||||
@param message the message to be sent
|
||||
@return the modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
char* prof_pre_chat_message_send(const char * const barejid, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a chat message has been sent
|
||||
* @param barejid Jabber ID of the message recipient
|
||||
* @param message The sent message
|
||||
*/
|
||||
Called after a chat message has been sent
|
||||
@param barejid Jabber ID of the message recipient
|
||||
@param message the sent message
|
||||
*/
|
||||
void prof_post_chat_message_send(const char * const barejid, const char *message);
|
||||
|
||||
/**
|
||||
* Called before a chat room message is displayed
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of message sender
|
||||
* @param message The received message
|
||||
* @return The new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
Called before a chat room message is displayed
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of message sender
|
||||
@param message the received message
|
||||
@return the new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
char* prof_pre_room_message_display(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a chat room message is displayed
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of the message sender
|
||||
* @param message The received message
|
||||
*/
|
||||
Called after a chat room message is displayed
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of the message sender
|
||||
@param message the received message
|
||||
*/
|
||||
void prof_post_room_message_display(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called before a chat room message is sent
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param message The message to be sent
|
||||
* @return The modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
Called before a chat room message is sent
|
||||
@param barejid Jabber ID of the room
|
||||
@param message the message to be sent
|
||||
@return the modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
char* prof_pre_room_message_send(const char * const barejid, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a chat room message has been sent
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param message The sent message
|
||||
*/
|
||||
Called after a chat room message has been sent
|
||||
@param barejid Jabber ID of the room
|
||||
@param message the sent message
|
||||
*/
|
||||
void prof_post_room_message_send(const char * const barejid, const char *message);
|
||||
|
||||
/**
|
||||
* Called when the server sends a chat room history message
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of the message sender
|
||||
* @param message The message to be sent
|
||||
* @param timestamp Time the message was originally sent to the room, in ISO8601 format
|
||||
*/
|
||||
Called when the server sends a chat room history message
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of the message sender
|
||||
@param message the message to be sent
|
||||
@param timestamp time the message was originally sent to the room, in ISO8601 format
|
||||
*/
|
||||
void prof_on_room_history_message(const char * const barejid, const char *const nick, const char *const message, const char *const timestamp);
|
||||
|
||||
/**
|
||||
* Called before a private chat room message is displayed
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of message sender
|
||||
* @param message The received message
|
||||
* @return The new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
Called before a private chat room message is displayed
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of message sender
|
||||
@param message the received message
|
||||
@return the new message to display, or NULL to preserve the original message
|
||||
*/
|
||||
char* prof_pre_priv_message_display(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a private chat room message is displayed
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of the message sender
|
||||
* @param message The received message
|
||||
*/
|
||||
Called after a private chat room message is displayed
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of the message sender
|
||||
@param message the received message
|
||||
*/
|
||||
void prof_post_priv_message_display(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called before a private chat room message is sent
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of message recipient
|
||||
* @param message The message to be sent
|
||||
* @return The modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
Called before a private chat room message is sent
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of message recipient
|
||||
@param message the message to be sent
|
||||
@return the modified or original message to send, or NULL to cancel sending of the message
|
||||
*/
|
||||
char* prof_pre_priv_message_send(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called after a private chat room message has been sent
|
||||
* @param barejid Jabber ID of the room
|
||||
* @param nick Nickname of the message recipient
|
||||
* @param message The sent message
|
||||
*/
|
||||
Called after a private chat room message has been sent
|
||||
@param barejid Jabber ID of the room
|
||||
@param nick nickname of the message recipient
|
||||
@param message the sent message
|
||||
*/
|
||||
void prof_post_priv_message_send(const char * const barejid, const char * const nick, const char *message);
|
||||
|
||||
/**
|
||||
* Called before an XMPP message stanza is sent
|
||||
* @param stanza The stanza to send
|
||||
* @return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
Called before an XMPP message stanza is sent
|
||||
@param stanza The stanza to send
|
||||
@return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
char* prof_on_message_stanza_send(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called when an XMPP message stanza is received
|
||||
* @param stanza The stanza received
|
||||
* @return 1 if CProof should continue to process the message stanza, 0 otherwise
|
||||
*/
|
||||
Called when an XMPP message stanza is received
|
||||
@param stanza The stanza received
|
||||
@return 1 if Profanity should continue to process the message stanza, 0 otherwise
|
||||
*/
|
||||
int prof_on_message_stanza_receive(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called before an XMPP presence stanza is sent
|
||||
* @param stanza The stanza to send
|
||||
* @return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
Called before an XMPP presence stanza is sent
|
||||
@param stanza The stanza to send
|
||||
@return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
char* prof_on_presence_stanza_send(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called when an XMPP presence stanza is received
|
||||
* @param stanza The stanza received
|
||||
* @return 1 if CProof should continue to process the presence stanza, 0 otherwise
|
||||
*/
|
||||
Called when an XMPP presence stanza is received
|
||||
@param stanza The stanza received
|
||||
@return 1 if Profanity should continue to process the presence stanza, 0 otherwise
|
||||
*/
|
||||
int prof_on_presence_stanza_receive(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called before an XMPP iq stanza is sent
|
||||
* @param stanza The stanza to send
|
||||
* @return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
Called before an XMPP iq stanza is sent
|
||||
@param stanza The stanza to send
|
||||
@return The new stanza to send, or NULL to preserve the original stanza
|
||||
*/
|
||||
char* prof_on_iq_stanza_send(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called when an XMPP iq stanza is received
|
||||
* @param stanza The stanza received
|
||||
* @return 1 if CProof should continue to process the iq stanza, 0 otherwise
|
||||
*/
|
||||
Called when an XMPP iq stanza is received
|
||||
@param stanza The stanza received
|
||||
@return 1 if Profanity should continue to process the iq stanza, 0 otherwise
|
||||
*/
|
||||
int prof_on_iq_stanza_receive(const char *const stanza);
|
||||
|
||||
/**
|
||||
* Called when a contact goes offline
|
||||
* @param barejid Jabber ID of the contact
|
||||
* @param resource The resource being disconnected
|
||||
* @param status The status message received with the offline presence, or NULL
|
||||
*/
|
||||
Called when a contact goes offline
|
||||
@param barejid Jabber ID of the contact
|
||||
@param resource the resource being disconnected
|
||||
@param status the status message received with the offline presence, or NULL
|
||||
*/
|
||||
void prof_on_contact_offline(const char *const barejid, const char *const resource, const char *const status);
|
||||
|
||||
/**
|
||||
* Called when a presence notification is received from a contact
|
||||
* @param barejid Jabber ID of the contact
|
||||
* @param resource The resource being disconnected
|
||||
* @param presence Presence of the contact, one of "chat", "online", "away", "xa" or "dnd"
|
||||
* @param status The status message received with the presence, or NULL
|
||||
* @param priority The priority associated with the resource
|
||||
*/
|
||||
Called when a presence notification is received from a contact
|
||||
@param barejid Jabber ID of the contact
|
||||
@param resource the resource being disconnected
|
||||
@param presence presence of the contact, one of "chat", "online", "away", "xa" or "dnd"
|
||||
@param status the status message received with the presence, or NULL
|
||||
@param priority the priority associated with the resource
|
||||
*/
|
||||
void prof_on_contact_presence(const char *const barejid, const char *const resource, const char *const presence, const char *const status, const int priority);
|
||||
|
||||
/**
|
||||
* Called when a chat window is focused
|
||||
* @param barejid Jabber ID of the chat window recipient
|
||||
*/
|
||||
Called when a chat window is focused
|
||||
@param barejid Jabber ID of the chat window recipient
|
||||
*/
|
||||
void prof_on_chat_win_focus(const char *const barejid);
|
||||
|
||||
/**
|
||||
* Called when a chat room window is focused
|
||||
* @param barejid Jabber ID of the room
|
||||
*/
|
||||
void prof_on_room_win_focus(const char *const barejid);
|
||||
Called when a chat room window is focused
|
||||
@param barejid Jabber ID of the room
|
||||
*/
|
||||
void prof_on_room_win_focus(const char *const barejid);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# CProof Python Plugins API documentation build configuration file.
|
||||
# Profanity Python Plugins API documentation build configuration file, created by
|
||||
# sphinx-quickstart on Thu Mar 17 23:01:31 2016.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
@@ -46,25 +47,25 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'CProof Python Plugins API'
|
||||
copyright = u'2025, CProof, based on Profanity by boothj5 et al.'
|
||||
author = u'CProof Developers'
|
||||
project = u'Profanity Python Plugins API'
|
||||
copyright = u'2016 - 2018, boothj5'
|
||||
author = u'boothj5'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0.15.0'
|
||||
version = u'0.5.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0.15.0'
|
||||
release = u'0.5.0'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
@@ -74,7 +75,7 @@ language = 'en'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build', 'modules.rst']
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
@@ -121,7 +122,7 @@ html_theme = 'default'
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = "CProof Python Plugins"
|
||||
html_title = "Profanity Python Plugins"
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
@@ -138,7 +139,7 @@ html_title = "CProof Python Plugins"
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
# html_static_path = ['_static']
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
@@ -201,7 +202,7 @@ html_show_copyright = False
|
||||
#html_search_scorer = 'scorer.js'
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'CProofPythonPluginsAPIdoc'
|
||||
htmlhelp_basename = 'ProfanityPythonPluginsAPIdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
@@ -223,8 +224,8 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'CProofPythonPluginsAPI.tex', u'CProof Python Plugins API Documentation',
|
||||
u'CProof Developers', 'manual'),
|
||||
(master_doc, 'ProfanityPythonPluginsAPI.tex', u'Profanity Python Plugins API Documentation',
|
||||
u'boothj5', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -253,7 +254,7 @@ latex_documents = [
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'cproofpythonpluginsapi', u'CProof Python Plugins API Documentation',
|
||||
(master_doc, 'profanitypythonpluginsapi', u'Profanity Python Plugins API Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
CProof Python Plugins API
|
||||
===========================
|
||||
Profanity Python Plugins API
|
||||
============================
|
||||
|
||||
The ``prof`` module describes functions that plugins may call to interact with CProof.
|
||||
The ``plugin`` module describes functions that plugins may define to be notified of various events in CProof.
|
||||
The ``prof`` module describes functions that plugins may call to interact with Profanity.
|
||||
The ``plugin`` module describes functions that plugins may define to be notified of various events in Profanity.
|
||||
|
||||
Contents:
|
||||
|
||||
@@ -11,5 +11,4 @@ Contents:
|
||||
|
||||
prof
|
||||
plugin
|
||||
|
||||
:ref:`genindex`
|
||||
* :ref:`genindex`
|
||||
|
||||
@@ -1,495 +1,360 @@
|
||||
"""
|
||||
The plugin module defines optional callback functions that CProof plugins can
|
||||
implement to handle events such as startup, shutdown, message processing, and
|
||||
presence updates.
|
||||
This page describes functions that plugins may implement to be called from Profanity on certain events. All functions are optional.
|
||||
|
||||
All functions are optional and are called by CProof when the corresponding event
|
||||
occurs. You need to define them in your module and they will automatically be used
|
||||
for callback events.
|
||||
Examples:
|
||||
::
|
||||
def prof_on_start():
|
||||
prof.cons_show("Profanity has started...")
|
||||
|
||||
To utilize full functionality, such as the ``prof.cons_show`` function, see ``prof``
|
||||
documentation and use::
|
||||
def prof_pre_room_message_display(room, nick, message):
|
||||
prof.cons_show("Manipulating chat room message before display...")
|
||||
new_message = message + " (added by plugin)"
|
||||
return new_message
|
||||
|
||||
import prof
|
||||
def prof_on_contact_presence(barejid, resource, presence, status, priority):
|
||||
notify_message = barejid + " is " + presence
|
||||
prof.notify(notify_message, 5, "Presence")
|
||||
"""
|
||||
|
||||
# Initialization and Lifecycle
|
||||
# ---------------------------
|
||||
def prof_init(version, status, account_name, fulljid):
|
||||
"""Called when a plugin is loaded, either when profanity is started, or when the ``/plugins load`` or ``/plugins install`` commands are called
|
||||
|
||||
def prof_init(version: str, status: str, account_name: str | None, fulljid: str | None) -> None:
|
||||
"""Initializes the plugin when loaded by CProof.
|
||||
|
||||
Called when CProof starts or when the plugin is loaded via the ``/plugins load``
|
||||
or ``/plugins install`` commands.
|
||||
|
||||
:param version: The version of CProof (e.g., ``"1.0.0"``).
|
||||
:param status: The package status of CProof (``"development"`` or ``"release"``).
|
||||
:param account_name: The account name of the logged-in user, or None if not logged in.
|
||||
:param fulljid: The full Jabber ID (barejid/resource) of the logged-in user, or None if not logged in.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_init(version: str, status: str, account_name: str | None, fulljid: str | None) -> None:
|
||||
prof.cons_show(f"MyPlugin for CProof {version} ({status}) has been loaded, account: {account_name}")
|
||||
:param version: the version of Profanity
|
||||
:param status: the package status of Profanity, ``"development"`` or ``"release"``
|
||||
:param account_name: account name of the currently logged in account, or ``None`` if not logged in
|
||||
:param fulljid: the users full Jabber ID (barejid and resource) if logged in, ``None`` otherwise
|
||||
:type version: str or unicode
|
||||
:type status: str or unicode
|
||||
:type account_name: str, unicode or None
|
||||
:type fulljid: str, unicode or None
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_start() -> None:
|
||||
"""Called when CProof starts.
|
||||
|
||||
Use this to perform setup tasks that should occur at application startup.
|
||||
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_start() -> None:
|
||||
prof.cons_show("CProof has started...")
|
||||
def prof_on_start():
|
||||
"""Called when Profanity is started
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_shutdown() -> None:
|
||||
"""Called when CProof is shutting down.
|
||||
|
||||
Use this to perform cleanup tasks before the application exits.
|
||||
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_shutdown() -> None:
|
||||
prof.cons_show("CProof is shutting down...")
|
||||
def prof_on_shutdown():
|
||||
"""Called when the user quits Profanity
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_unload() -> None:
|
||||
"""Called when the plugin is unloaded via the ``/plugins unload`` command.
|
||||
|
||||
Use this to clean up plugin-specific resources.
|
||||
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_unload() -> None:
|
||||
prof.cons_show("Plugin unloaded")
|
||||
def prof_on_unload():
|
||||
"""Called when a plugin is unloaded with the ``/plugins unload`` command
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_connect(account_name: str, fulljid: str) -> None:
|
||||
"""Called when a user connects to CProof with an account.
|
||||
|
||||
:param account_name: The account name used for login.
|
||||
:param fulljid: The full Jabber ID (barejid/resource) of the connected account.
|
||||
:return: None
|
||||
def prof_on_connect(account_name, fulljid):
|
||||
"""Called when the user connects with an account
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_connect(account_name: str, fulljid: str) -> None:
|
||||
prof.cons_show(f"Connected as {account_name} ({fulljid})")
|
||||
:param account_name: account name of the account used for logging in
|
||||
:param fulljid: the full Jabber ID (barejid and resource) of the account
|
||||
:type account_name: str or unicode
|
||||
:type fulljid: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_disconnect(account_name: str, fulljid: str) -> None:
|
||||
"""Called when a user disconnects an account from CProof.
|
||||
|
||||
:param account_name: The account name being disconnected.
|
||||
:param fulljid: The full Jabber ID (barejid/resource) of the disconnected account.
|
||||
:return: None
|
||||
def prof_on_disconnect(account_name, fulljid):
|
||||
"""Called when the user disconnects an account
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_disconnect(account_name: str, fulljid: str) -> None:
|
||||
prof.cons_show(f"Disconnected {account_name} ({fulljid})")
|
||||
:param account_name: account name of the account being disconnected
|
||||
:param fulljid: the full Jabber ID (barejid and resource) of the account
|
||||
:type account_name: str or unicode
|
||||
:type fulljid: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
# Chat Message Handlers
|
||||
# ---------------------
|
||||
|
||||
def prof_pre_chat_message_display(barejid: str, resource: str, message: str) -> str | None:
|
||||
"""Called before a chat message is displayed in a chat window.
|
||||
def prof_pre_chat_message_display(barejid, resource, message):
|
||||
"""Called before a chat message is displayed
|
||||
|
||||
Allows the plugin to modify or cancel the message display.
|
||||
|
||||
:param barejid: The Jabber ID of the message sender (e.g., ``bob@example.com``).
|
||||
:param resource: The sender's resource (e.g., ``laptop``).
|
||||
:param message: The received message.
|
||||
:return: The modified message to display, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_chat_message_display(barejid: str, resource: str, message: str) -> str | None:
|
||||
new_message = f"{message} (from {barejid})"
|
||||
return new_message
|
||||
:param barejid: Jabber ID of the message sender
|
||||
:param resource: resource of the message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type resource: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the new message to display, or ``None`` to preserve the original message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_chat_message_display(barejid: str, resource: str, message: str) -> None:
|
||||
"""Called after a chat message is displayed in a chat window.
|
||||
|
||||
Use this to perform actions after the message is shown.
|
||||
def prof_post_chat_message_display(barejid, resource, message):
|
||||
"""Called after a chat message is displayed
|
||||
|
||||
:param barejid: The Jabber ID of the message sender (e.g., ``bob@example.com``).
|
||||
:param resource: The sender's resource (e.g., ``laptop``).
|
||||
:param message: The displayed message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_chat_message_display(barejid: str, resource: str, message: str) -> None:
|
||||
prof.cons_show(f"Displayed message from {barejid}/{resource}: {message}")
|
||||
:param barejid: Jabber ID of the message sender
|
||||
:param resource: resource of the message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type resource: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_pre_chat_message_send(barejid: str, message: str) -> str | None:
|
||||
"""Called before a chat message is sent from CProof.
|
||||
|
||||
Allows the plugin to modify or cancel the message.
|
||||
def prof_pre_chat_message_send(barejid, message):
|
||||
"""Called before a chat message is sent
|
||||
|
||||
:param barejid: The Jabber ID of the recipient (e.g., ``bob@example.com``).
|
||||
:param message: The message to be sent.
|
||||
:return: The modified message to send, or None to cancel sending.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_chat_message_send(barejid: str, message: str) -> str | None:
|
||||
return f"{message} (sent by plugin)"
|
||||
:param barejid: Jabber ID of the message recipient
|
||||
:param message: the message to be sent
|
||||
:type barejid: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the modified or original message to send, or ``None`` to cancel sending of the message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_chat_message_send(barejid: str, message: str) -> None:
|
||||
"""Called after a chat message is sent from CProof.
|
||||
|
||||
Use this to log or react to sent messages.
|
||||
def prof_post_chat_message_send(barejid, message):
|
||||
"""Called after a chat message has been sent
|
||||
|
||||
:param barejid: The Jabber ID of the recipient (e.g., ``bob@example.com``).
|
||||
:param message: The sent message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_chat_message_send(barejid: str, message: str) -> None:
|
||||
prof.cons_show(f"Sent to {barejid}: {message}")
|
||||
:param barejid: Jabber ID of the message recipient
|
||||
:param message: the sent message
|
||||
:type barejid: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
# Room Message Handlers
|
||||
# ---------------------
|
||||
|
||||
def prof_pre_room_message_display(barejid: str, nick: str, message: str) -> str | None:
|
||||
"""Called before a chat room message is displayed.
|
||||
def prof_pre_room_message_display(barejid, nick, message):
|
||||
"""Called before a chat room message is displayed
|
||||
|
||||
Allows the plugin to modify or cancel the message display.
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message sender.
|
||||
:param message: The received message.
|
||||
:return: The modified message to display, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_room_message_display(barejid: str, nick: str, message: str) -> str | None:
|
||||
return f"{message} (from {nick})"
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the new message to display, or ``None`` to preserve the original message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_room_message_display(barejid: str, nick: str, message: str) -> None:
|
||||
"""Called after a chat room message is displayed.
|
||||
|
||||
Use this to perform actions after the message is shown.
|
||||
def prof_post_room_message_display(barejid, nick, message):
|
||||
"""Called after a chat room message is displayed
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message sender.
|
||||
:param message: The displayed message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_room_message_display(barejid: str, nick: str, message: str) -> None:
|
||||
prof.cons_show(f"Displayed in {barejid} from {nick}: {message}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of the message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_pre_room_message_send(barejid: str, message: str) -> str | None:
|
||||
"""Called before a chat room message is sent.
|
||||
|
||||
Allows the plugin to modify or cancel the message.
|
||||
def prof_pre_room_message_send(barejid, message):
|
||||
"""Called before a chat room message is sent
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param message: The message to be sent.
|
||||
:return: The modified message to send, or None to cancel sending.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_room_message_send(barejid: str, message: str) -> str | None:
|
||||
return f"{message} (sent by plugin)"
|
||||
:param barejid: Jabber ID of the room
|
||||
:param message: the message to be sent
|
||||
:type barejid: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the modified or original message to send, or ``None`` to cancel sending of the message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_room_message_send(barejid: str, message: str) -> None:
|
||||
"""Called after a chat room message is sent.
|
||||
|
||||
Use this to log or react to sent messages.
|
||||
def prof_post_room_message_send(barejid, message):
|
||||
"""Called after a chat room message has been sent
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param message: The sent message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_room_message_send(barejid: str, message: str) -> None:
|
||||
prof.cons_show(f"Sent to {barejid}: {message}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:param message: the sent message
|
||||
:type barejid: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_room_history_message(barejid: str, nick: str, message: str, timestamp: str) -> None:
|
||||
"""Called when a chat room history message is received from the server.
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message sender.
|
||||
:param message: The received message.
|
||||
:param timestamp: The message's original send time in ISO 8601 format (e.g., ``2025-09-10T19:45:00Z``).
|
||||
:return: None
|
||||
def prof_on_room_history_message(barejid, nick, message, timestamp):
|
||||
"""Called when the server sends a chat room history message
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_room_history_message(barejid: str, nick: str, message: str, timestamp: str) -> None:
|
||||
prof.cons_show(f"History in {barejid} from {nick} at {timestamp}: {message}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of the message sender
|
||||
:param message: the message to be sent
|
||||
:param timestamp: time the message was originally sent to the room, in ISO8601 format
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
:type timestamp: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
# Private Message Handlers
|
||||
# ------------------------
|
||||
|
||||
def prof_pre_priv_message_display(barejid: str, nick: str, message: str) -> str | None:
|
||||
"""Called before a private chat room message is displayed.
|
||||
def prof_pre_priv_message_display(barejid, nick, message):
|
||||
"""Called before a private chat room message is displayed
|
||||
|
||||
Allows the plugin to modify or cancel the message display.
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message sender.
|
||||
:param message: The received message.
|
||||
:return: The modified message to display, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_priv_message_display(barejid: str, nick: str, message: str) -> str | None:
|
||||
return f"{message} (private from {nick})"
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the new message to display, or ``None`` to preserve the original message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_priv_message_display(barejid: str, nick: str, message: str) -> None:
|
||||
"""Called after a private chat room message is displayed.
|
||||
|
||||
Use this to perform actions after the message is shown.
|
||||
def prof_post_priv_message_display(barejid, nick, message):
|
||||
"""Called after a private chat room message is displayed
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message sender.
|
||||
:param message: The displayed message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_priv_message_display(barejid: str, nick: str, message: str) -> None:
|
||||
prof.cons_show(f"Displayed private in {barejid} from {nick}: {message}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of the message sender
|
||||
:param message: the received message
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_pre_priv_message_send(barejid: str, nick: str, message: str) -> str | None:
|
||||
"""Called before a private chat room message is sent.
|
||||
|
||||
Allows the plugin to modify or cancel the message.
|
||||
def prof_pre_priv_message_send(barejid, nick, message):
|
||||
"""Called before a private chat room message is sent
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message recipient.
|
||||
:param message: The message to be sent.
|
||||
:return: The modified message to send, or None to cancel sending.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_pre_priv_message_send(barejid: str, nick: str, message: str) -> str | None:
|
||||
return f"{message} (private to {nick})"
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of message recipient
|
||||
:param message: the message to be sent
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
:return: the modified or original message to send, or ``None`` to cancel sending of the message
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_post_priv_message_send(barejid: str, nick: str, message: str) -> None:
|
||||
"""Called after a private chat room message is sent.
|
||||
|
||||
Use this to log or react to sent messages.
|
||||
def prof_post_priv_message_send(barejid, nick, message):
|
||||
"""Called after a private chat room message has been sent
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:param nick: The nickname of the message recipient.
|
||||
:param message: The sent message.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_post_priv_message_send(barejid: str, nick: str, message: str) -> None:
|
||||
prof.cons_show(f"Sent private to {nick} in {barejid}: {message}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:param nick: nickname of the message recipient
|
||||
:param message: the sent message
|
||||
:type barejid: str or unicode
|
||||
:type nick: str or unicode
|
||||
:type message: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
# Stanza Handlers
|
||||
# ---------------
|
||||
|
||||
def prof_on_message_stanza_send(stanza: str) -> str | None:
|
||||
"""Called before an XMPP message stanza is sent.
|
||||
def prof_on_message_stanza_send(stanza):
|
||||
"""Called before an XMPP message stanza is sent
|
||||
|
||||
Allows the plugin to modify or cancel the stanza.
|
||||
|
||||
:param stanza: The XMPP message stanza to send.
|
||||
:return: The modified stanza to send, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_message_stanza_send(stanza: str) -> str | None:
|
||||
prof.cons_show(f"Sending message stanza: {stanza}")
|
||||
return stanza
|
||||
:param stanza: The stanza to send
|
||||
:type stanza: str or unicode
|
||||
:return: The new stanza to send, or ``None`` to preserve the original stanza
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_message_stanza_receive(stanza: str) -> bool:
|
||||
"""Called when an XMPP message stanza is received.
|
||||
|
||||
Allows the plugin to control whether CProof processes the stanza.
|
||||
def prof_on_message_stanza_receive(stanza):
|
||||
"""Called when an XMPP message stanza is received
|
||||
|
||||
:param stanza: The received XMPP message stanza.
|
||||
:return: True to allow CProof to process the stanza, False to block processing.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_message_stanza_receive(stanza: str) -> bool:
|
||||
prof.cons_show(f"Received message stanza: {stanza}")
|
||||
return True
|
||||
:param stanza: The stanza received
|
||||
:type stanza: str or unicode
|
||||
:return: ``True`` if Profanity should continue to process the message stanza, ``False`` otherwise
|
||||
:rtype: boolean
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_presence_stanza_send(stanza: str) -> str | None:
|
||||
"""Called before an XMPP presence stanza is sent.
|
||||
|
||||
Allows the plugin to modify or cancel the stanza.
|
||||
def prof_on_presence_stanza_send(stanza):
|
||||
"""Called before an XMPP presence stanza is sent
|
||||
|
||||
:param stanza: The XMPP presence stanza to send.
|
||||
:return: The modified stanza to send, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_presence_stanza_send(stanza: str) -> str | None:
|
||||
prof.cons_show(f"Sending presence stanza: {stanza}")
|
||||
return stanza
|
||||
:param stanza: The stanza to send
|
||||
:type stanza: str or unicode
|
||||
:return: The new stanza to send, or ``None`` to preserve the original stanza
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_presence_stanza_receive(stanza: str) -> bool:
|
||||
"""Called when an XMPP presence stanza is received.
|
||||
|
||||
Allows the plugin to control whether CProof processes the stanza.
|
||||
def prof_on_presence_stanza_receive(stanza):
|
||||
"""Called when an XMPP presence stanza is received
|
||||
|
||||
:param stanza: The received XMPP presence stanza.
|
||||
:return: True to allow CProof to process the stanza, False to block processing.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_presence_stanza_receive(stanza: str) -> bool:
|
||||
prof.cons_show(f"Received presence stanza: {stanza}")
|
||||
return True
|
||||
:param stanza: The stanza received
|
||||
:type stanza: str or unicode
|
||||
:return: ``True`` if Profanity should continue to process the presence stanza, ``False`` otherwise
|
||||
:rtype: boolean
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_iq_stanza_send(stanza: str) -> str | None:
|
||||
"""Called before an XMPP IQ stanza is sent.
|
||||
|
||||
Allows the plugin to modify or cancel the stanza.
|
||||
def prof_on_iq_stanza_send(stanza):
|
||||
"""Called before an XMPP iq stanza is sent
|
||||
|
||||
:param stanza: The XMPP IQ stanza to send.
|
||||
:return: The modified stanza to send, or None to preserve the original.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_iq_stanza_send(stanza: str) -> str | None:
|
||||
prof.cons_show(f"Sending IQ stanza: {stanza}")
|
||||
return stanza
|
||||
:param stanza: The stanza to send
|
||||
:type stanza: str or unicode
|
||||
:return: The new stanza to send, or ``None`` to preserve the original stanza
|
||||
:rtype: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_iq_stanza_receive(stanza: str) -> bool:
|
||||
"""Called when an XMPP IQ stanza is received.
|
||||
|
||||
Allows the plugin to control whether CProof processes the stanza.
|
||||
def prof_on_iq_stanza_receive(stanza):
|
||||
"""Called when an XMPP iq stanza is received
|
||||
|
||||
:param stanza: The received XMPP IQ stanza.
|
||||
:return: True to allow CProof to process the stanza, False to block processing.
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_iq_stanza_receive(stanza: str) -> bool:
|
||||
prof.cons_show(f"Received IQ stanza: {stanza}")
|
||||
return True
|
||||
:param stanza: The stanza received
|
||||
:type stanza: str or unicode
|
||||
:return: ``True`` if Profanity should continue to process the iq stanza, ``False`` otherwise
|
||||
:rtype: boolean
|
||||
"""
|
||||
pass
|
||||
|
||||
# Contact Presence Handlers
|
||||
# -------------------------
|
||||
|
||||
def prof_on_contact_offline(barejid: str, resource: str, status: str | None) -> None:
|
||||
"""Called when a contact goes offline.
|
||||
def prof_on_contact_offline(barejid, resource, status):
|
||||
"""Called when a contact goes offline
|
||||
|
||||
:param barejid: The Jabber ID of the contact (e.g., ``bob@example.com``).
|
||||
:param resource: The resource being disconnected (e.g., ``laptop``).
|
||||
:param status: The status message received with the offline presence, or None.
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_contact_offline(barejid: str, resource: str, status: str | None) -> None:
|
||||
prof.cons_show(f"{barejid}/{resource} went offline: {status or 'No status'}")
|
||||
:param barejid: Jabber ID of the contact
|
||||
:param resource: the resource being disconnected
|
||||
:param status: the status message received with the offline presence, or ``None``
|
||||
:type barejid: str or unicode
|
||||
:type resource: str or unicode
|
||||
:type status: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_contact_presence(barejid: str, resource: str, presence: str, status: str | None, priority: int) -> None:
|
||||
"""Called when a presence notification is received from a contact.
|
||||
|
||||
:param barejid: The Jabber ID of the contact (e.g., ``bob@example.com``).
|
||||
:param resource: The resource of the contact (e.g., ``laptop``).
|
||||
:param presence: The contact's presence (``"chat"``, ``"online"``, ``"away"``, ``"xa"``, or ``"dnd"``).
|
||||
:param status: The status message received with the presence, or None.
|
||||
:param priority: The priority associated with the resource.
|
||||
:return: None
|
||||
def prof_on_contact_presence(barejid, resource, presence, status, priority):
|
||||
"""Called when a presence notification is received from a contact
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_contact_presence(barejid: str, resource: str, presence: str, status: str | None, priority: int) -> None:
|
||||
prof.notify(f"{barejid} is {presence}", 5000, "Presence")
|
||||
:param barejid: Jabber ID of the contact
|
||||
:param resource: the resource being disconnected
|
||||
:param presence: presence of the contact, one of ``"chat"``, ``"online"``, ``"away"``, ``"xa"`` or ``"dnd"``
|
||||
:param status: the status message received with the presence, or ``None``
|
||||
:param priority: the priority associated with the resource
|
||||
:type barejid: str or unicode
|
||||
:type resource: str or unicode
|
||||
:type presence: str or unicode
|
||||
:type status: str or unicode
|
||||
:type priority: int
|
||||
"""
|
||||
pass
|
||||
|
||||
# Window Focus Handlers
|
||||
# ---------------------
|
||||
|
||||
def prof_on_chat_win_focus(barejid: str) -> None:
|
||||
"""Called when a chat window is focused.
|
||||
def prof_on_chat_win_focus(barejid):
|
||||
"""Called when a chat window is focused
|
||||
|
||||
:param barejid: The Jabber ID of the chat window recipient (e.g., ``bob@example.com``).
|
||||
:return: None
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_chat_win_focus(barejid: str) -> None:
|
||||
prof.cons_show(f"Focused chat window for {barejid}")
|
||||
:param barejid: Jabber ID of the chat window recipient
|
||||
:type barejid: str or unicode
|
||||
"""
|
||||
pass
|
||||
|
||||
def prof_on_room_win_focus(barejid: str) -> None:
|
||||
"""Called when a chat room window is focused.
|
||||
|
||||
:param barejid: The Jabber ID of the room (e.g., ``chat@conference.example.com``).
|
||||
:return: None
|
||||
def prof_on_room_win_focus(barejid):
|
||||
"""Called when a chat room window is focused
|
||||
|
||||
Example::
|
||||
|
||||
def prof_on_room_win_focus(barejid: str) -> None:
|
||||
prof.cons_show(f"Focused room window for {barejid}")
|
||||
:param barejid: Jabber ID of the room
|
||||
:type barejid: str or unicode
|
||||
"""
|
||||
pass
|
||||
pass
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
66
ci-build.sh
66
ci-build.sh
@@ -44,43 +44,49 @@ ARCH="$(uname | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
case "$ARCH" in
|
||||
linux*)
|
||||
# Reduced set of configurations for faster CI
|
||||
tests=(
|
||||
# 1. Full build (all features enabled)
|
||||
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
|
||||
--enable-omemo --enable-plugins --enable-c-plugins
|
||||
--enable-python-plugins --with-xscreensaver --enable-omemo-qrcode --enable-gdk-pixbuf"
|
||||
# 2. Minimal build (all optional features disabled)
|
||||
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
|
||||
--disable-omemo --disable-plugins --disable-c-plugins
|
||||
--disable-python-plugins --without-xscreensaver --disable-omemo-qrcode --disable-gdk-pixbuf"
|
||||
# 3. No encryption (disable otr, pgp, omemo)
|
||||
"--disable-pgp --disable-otr --disable-omemo --disable-omemo-qrcode"
|
||||
# 4. No plugins
|
||||
"--disable-plugins --disable-c-plugins --disable-python-plugins"
|
||||
# 5. Default configuration
|
||||
""
|
||||
)
|
||||
--disable-python-plugins --without-xscreensaver"
|
||||
"--disable-notifications"
|
||||
"--disable-icons-and-clipboard"
|
||||
"--disable-otr"
|
||||
"--disable-pgp"
|
||||
"--disable-omemo --disable-omemo-qrcode"
|
||||
"--disable-pgp --disable-otr"
|
||||
"--disable-pgp --disable-otr --disable-omemo"
|
||||
"--disable-plugins"
|
||||
"--disable-python-plugins"
|
||||
"--disable-c-plugins"
|
||||
"--disable-c-plugins --disable-python-plugins"
|
||||
"--without-xscreensaver"
|
||||
"--disable-gdk-pixbuf"
|
||||
"")
|
||||
source /etc/profile.d/debuginfod.sh 2>/dev/null || true
|
||||
;;
|
||||
darwin*)
|
||||
# Reduced set of configurations for faster CI
|
||||
tests=(
|
||||
# 1. Full build (all features enabled)
|
||||
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
|
||||
--enable-omemo --enable-plugins --enable-c-plugins
|
||||
--enable-python-plugins"
|
||||
# 2. Minimal build (all optional features disabled)
|
||||
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
|
||||
--disable-omemo --disable-plugins --disable-c-plugins
|
||||
--disable-python-plugins"
|
||||
# 3. No encryption (disable otr, pgp, omemo)
|
||||
"--disable-notifications"
|
||||
"--disable-icons-and-clipboard"
|
||||
"--disable-otr"
|
||||
"--disable-pgp"
|
||||
"--disable-omemo"
|
||||
"--disable-pgp --disable-otr"
|
||||
"--disable-pgp --disable-otr --disable-omemo"
|
||||
# 4. No plugins
|
||||
"--disable-plugins --disable-c-plugins --disable-python-plugins"
|
||||
# 5. Default configuration
|
||||
""
|
||||
)
|
||||
"--disable-plugins"
|
||||
"--disable-python-plugins"
|
||||
"--disable-c-plugins"
|
||||
"--disable-c-plugins --disable-python-plugins"
|
||||
"")
|
||||
;;
|
||||
openbsd*)
|
||||
MAKE="gmake"
|
||||
@@ -90,23 +96,25 @@ case "$ARCH" in
|
||||
# src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99
|
||||
CC="egcc -std=gnu99 -fexec-charset=UTF-8"
|
||||
|
||||
# Reduced set of configurations for faster CI
|
||||
tests=(
|
||||
# 1. Full build (all features enabled)
|
||||
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
|
||||
--enable-omemo --enable-plugins --enable-c-plugins
|
||||
--enable-python-plugins"
|
||||
# 2. Minimal build (all optional features disabled)
|
||||
"--disable-notifications --disable-icons-and-clipboard --disable-otr --disable-pgp
|
||||
--disable-omemo --disable-plugins --disable-c-plugins
|
||||
--disable-python-plugins"
|
||||
# 3. No encryption (disable otr, pgp, omemo)
|
||||
"--disable-notifications"
|
||||
"--disable-icons-and-clipboard"
|
||||
"--disable-otr"
|
||||
"--disable-pgp"
|
||||
"--disable-omemo"
|
||||
"--disable-pgp --disable-otr"
|
||||
"--disable-pgp --disable-otr --disable-omemo"
|
||||
# 4. No plugins
|
||||
"--disable-plugins --disable-c-plugins --disable-python-plugins"
|
||||
# 5. Default configuration
|
||||
""
|
||||
)
|
||||
"--disable-plugins"
|
||||
"--disable-python-plugins"
|
||||
"--disable-c-plugins"
|
||||
"--disable-c-plugins --disable-python-plugins"
|
||||
"")
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
10
configure.ac
10
configure.ac
@@ -374,13 +374,9 @@ PKG_CHECK_MODULES([cmocka], [cmocka], [],
|
||||
AM_CONDITIONAL([HAVE_STABBER], [false])
|
||||
AC_CHECK_LIB([stabber], [stbbr_start], [AM_CONDITIONAL([HAVE_STABBER], [true])],
|
||||
[AC_MSG_NOTICE([libstabber not found, will not be able to run functional tests])])
|
||||
dnl Check for forkpty (needed for functional tests PTY handling)
|
||||
dnl On Linux it's in libutil, on some BSDs it's in libc
|
||||
AM_CONDITIONAL([HAVE_FORKPTY], [false])
|
||||
AC_CHECK_LIB([util], [forkpty], [AM_CONDITIONAL([HAVE_FORKPTY], [true]) FORKPTY_LIB="-lutil"],
|
||||
[AC_CHECK_FUNC([forkpty], [AM_CONDITIONAL([HAVE_FORKPTY], [true]) FORKPTY_LIB=""],
|
||||
[AC_MSG_NOTICE([forkpty not found, will not be able to run functional tests])])])
|
||||
AC_SUBST([FORKPTY_LIB])
|
||||
AM_CONDITIONAL([HAVE_EXPECT], [false])
|
||||
AC_CHECK_LIB([expect], [exp_expectl], [AM_CONDITIONAL([HAVE_EXPECT], [true])],
|
||||
[AC_MSG_NOTICE([libexpect not found, will not be able to run functional tests])])
|
||||
|
||||
## Default parameters
|
||||
AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3"
|
||||
|
||||
358
prof.supp
358
prof.supp
@@ -8,83 +8,6 @@
|
||||
# * python suppressions file from https://github.com/python/cpython/blob/main/Misc/valgrind-python.supp
|
||||
#
|
||||
|
||||
# ============================================
|
||||
# Functional tests suppressions (stabber/pthread)
|
||||
# ============================================
|
||||
|
||||
{
|
||||
stabber_pthread_create
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:calloc
|
||||
...
|
||||
fun:allocate_dtv
|
||||
fun:_dl_allocate_tls
|
||||
...
|
||||
fun:pthread_create*
|
||||
...
|
||||
fun:server_run
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
stabber_server_run
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:calloc
|
||||
...
|
||||
fun:pthread_create*
|
||||
...
|
||||
obj:*/libstabber*
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
glib_time_zone_cache
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:g_time_zone_new*
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
glib_time_zone_local
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:g_strdup
|
||||
...
|
||||
fun:g_time_zone_new_identifier
|
||||
fun:g_time_zone_new_local
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
glib_date_time_format
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
glib_date_time_format_locale
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:*alloc
|
||||
...
|
||||
obj:*/libglib*
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Original suppressions
|
||||
# ============================================
|
||||
|
||||
{
|
||||
_dl_init
|
||||
Memcheck:Leak
|
||||
@@ -2780,284 +2703,3 @@
|
||||
fun:calloc
|
||||
fun:_dl_allocate_tls
|
||||
}
|
||||
|
||||
# pthread TLS allocation in stabber server threads
|
||||
{
|
||||
pthread_create_tls_stabber
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:calloc
|
||||
...
|
||||
fun:allocate_dtv
|
||||
fun:_dl_allocate_tls
|
||||
fun:allocate_stack
|
||||
fun:pthread_create*
|
||||
fun:server_run
|
||||
}
|
||||
|
||||
# expect/tcl library allocations
|
||||
{
|
||||
tcl_alloc_expect
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:malloc
|
||||
...
|
||||
fun:TclpAlloc
|
||||
fun:Tcl_Alloc
|
||||
...
|
||||
fun:exp_expectl
|
||||
}
|
||||
|
||||
{
|
||||
exp_printify
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:malloc
|
||||
...
|
||||
fun:exp_printify
|
||||
...
|
||||
fun:exp_expectl
|
||||
}
|
||||
|
||||
|
||||
# Additional suppressions for functional tests
|
||||
|
||||
# libexpect still reachable allocations
|
||||
{
|
||||
exp_spawnv_malloc
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
...
|
||||
fun:exp_spawnv
|
||||
fun:exp_spawnl
|
||||
}
|
||||
|
||||
{
|
||||
exp_spawnl_realloc
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:realloc
|
||||
...
|
||||
fun:exp_spawnv
|
||||
fun:exp_spawnl
|
||||
}
|
||||
|
||||
# libtcl memory pool allocations (expected)
|
||||
{
|
||||
tcl_alloc_pool
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
...
|
||||
fun:TclpAlloc
|
||||
fun:Tcl_Alloc
|
||||
}
|
||||
|
||||
{
|
||||
tcl_alloc_pool_calloc
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:calloc
|
||||
...
|
||||
fun:TclpAlloc
|
||||
fun:Tcl_Alloc
|
||||
}
|
||||
|
||||
# pthread thread-local storage (normal for multi-threaded programs)
|
||||
{
|
||||
pthread_tls_allocate_dtv
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: possible
|
||||
fun:calloc
|
||||
fun:calloc
|
||||
fun:allocate_dtv
|
||||
fun:_dl_allocate_tls
|
||||
fun:allocate_stack
|
||||
fun:pthread_create*
|
||||
}
|
||||
|
||||
# glib static initializations
|
||||
{
|
||||
glib_hash_table_init
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
fun:g_malloc
|
||||
fun:g_hash_table_new_full
|
||||
...
|
||||
fun:call_init
|
||||
fun:_dl_init
|
||||
}
|
||||
|
||||
{
|
||||
glib_array_init
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:realloc
|
||||
fun:g_realloc
|
||||
...
|
||||
fun:call_init
|
||||
fun:_dl_init
|
||||
}
|
||||
|
||||
# fdopen/fopen allocations (FILE* buffers - normal)
|
||||
{
|
||||
fdopen_file_buffer
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
...
|
||||
fun:fdopen*
|
||||
}
|
||||
|
||||
{
|
||||
fopen_file_buffer
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
...
|
||||
fun:fopen*
|
||||
}
|
||||
|
||||
# stabber log_init (server log file)
|
||||
{
|
||||
stabber_log_init
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:malloc
|
||||
...
|
||||
fun:log_init
|
||||
fun:server_run
|
||||
}
|
||||
|
||||
# glib time zone (static allocation)
|
||||
{
|
||||
g_time_zone_new_local
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
...
|
||||
fun:g_time_zone_new_local
|
||||
}
|
||||
|
||||
{
|
||||
g_time_zone_array
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: reachable
|
||||
fun:realloc
|
||||
fun:g_realloc
|
||||
...
|
||||
fun:g_array_sized_new
|
||||
fun:g_time_zone_new_identifier
|
||||
}
|
||||
|
||||
# stabber log g_date_time_format invalid read - benign race in logging
|
||||
{
|
||||
stabber_g_date_time_format_invalid_read
|
||||
Memcheck:Addr1
|
||||
...
|
||||
fun:g_date_time_format
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
stabber_g_date_time_format_invalid_read2
|
||||
Memcheck:Addr2
|
||||
...
|
||||
fun:g_date_time_format
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
stabber_g_date_time_format_invalid_read4
|
||||
Memcheck:Addr4
|
||||
...
|
||||
fun:g_date_time_format
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
stabber_g_date_time_format_invalid_read8
|
||||
Memcheck:Addr8
|
||||
...
|
||||
fun:g_date_time_format
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
# More generic suppression for g_date_time_format race condition
|
||||
{
|
||||
g_date_time_format_cond
|
||||
Memcheck:Cond
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
g_date_time_format_value1
|
||||
Memcheck:Value1
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
g_date_time_format_value2
|
||||
Memcheck:Value2
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
g_date_time_format_value4
|
||||
Memcheck:Value4
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
g_date_time_format_value8
|
||||
Memcheck:Value8
|
||||
...
|
||||
fun:g_date_time_format
|
||||
...
|
||||
}
|
||||
|
||||
# Suppress all Addr errors in log_println
|
||||
{
|
||||
log_println_addr1
|
||||
Memcheck:Addr1
|
||||
...
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
log_println_addr2
|
||||
Memcheck:Addr2
|
||||
...
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
log_println_addr4
|
||||
Memcheck:Addr4
|
||||
...
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
{
|
||||
log_println_addr8
|
||||
Memcheck:Addr8
|
||||
...
|
||||
fun:log_println
|
||||
...
|
||||
}
|
||||
|
||||
@@ -136,7 +136,6 @@ static char* _mood_autocomplete(ProfWin* window, const char* const input, gboole
|
||||
static char* _strophe_autocomplete(ProfWin* window, const char* const input, gboolean previous);
|
||||
static char* _adhoc_cmd_autocomplete(ProfWin* window, const char* const input, gboolean previous);
|
||||
static char* _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous);
|
||||
static char* _force_encryption_autocomplete(ProfWin* window, const char* const input, gboolean previous);
|
||||
|
||||
static char* _script_autocomplete_func(const char* const prefix, gboolean previous, void* context);
|
||||
|
||||
@@ -297,8 +296,6 @@ static Autocomplete vcard_name_ac;
|
||||
static Autocomplete vcard_set_param_ac;
|
||||
static Autocomplete vcard_togglable_param_ac;
|
||||
static Autocomplete vcard_address_type_ac;
|
||||
static Autocomplete force_encryption_ac;
|
||||
static Autocomplete force_encryption_policy_ac;
|
||||
|
||||
static Autocomplete* all_acs[] = {
|
||||
&commands_ac,
|
||||
@@ -451,8 +448,6 @@ static Autocomplete* all_acs[] = {
|
||||
&vcard_set_param_ac,
|
||||
&vcard_togglable_param_ac,
|
||||
&vcard_address_type_ac,
|
||||
&force_encryption_ac,
|
||||
&force_encryption_policy_ac
|
||||
};
|
||||
|
||||
static GHashTable* ac_funcs = NULL;
|
||||
@@ -1348,13 +1343,6 @@ cmd_ac_init(void)
|
||||
autocomplete_add(vcard_address_type_ac, "domestic");
|
||||
autocomplete_add(vcard_address_type_ac, "international");
|
||||
|
||||
autocomplete_add(force_encryption_ac, "on");
|
||||
autocomplete_add(force_encryption_ac, "off");
|
||||
autocomplete_add(force_encryption_ac, "policy");
|
||||
|
||||
autocomplete_add(force_encryption_policy_ac, "block");
|
||||
autocomplete_add(force_encryption_policy_ac, "resend-to-confirm");
|
||||
|
||||
if (ac_funcs != NULL)
|
||||
g_hash_table_destroy(ac_funcs);
|
||||
ac_funcs = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
@@ -1427,7 +1415,6 @@ cmd_ac_init(void)
|
||||
g_hash_table_insert(ac_funcs, "/win", _win_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/wins", _wins_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/wintitle", _wintitle_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/force-encryption", _force_encryption_autocomplete);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1675,7 +1662,7 @@ char*
|
||||
cmd_ac_complete_filepath(const char* const input, char* const startstr, gboolean previous)
|
||||
{
|
||||
unsigned int output_off = 0;
|
||||
char* tmp = NULL;
|
||||
char* tmp;
|
||||
|
||||
// strip command
|
||||
char* inpcp = (char*)input + strlen(startstr);
|
||||
@@ -1687,36 +1674,38 @@ cmd_ac_complete_filepath(const char* const input, char* const startstr, gboolean
|
||||
|
||||
// strip quotes
|
||||
if (*inpcp == '"') {
|
||||
tmp = strrchr(inpcp + 1, '"');
|
||||
tmp = strchr(inpcp + 1, '"');
|
||||
if (tmp) {
|
||||
*tmp = '\0';
|
||||
}
|
||||
tmp = strdup(inpcp + 1);
|
||||
free(inpcp);
|
||||
inpcp = tmp;
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
// expand ~ to $HOME
|
||||
if (inpcp[0] == '~' && inpcp[1] == '/') {
|
||||
char* home = getenv("HOME");
|
||||
if (!home) {
|
||||
tmp = g_strdup_printf("%s/%sfoo", getenv("HOME"), inpcp + 2);
|
||||
if (!tmp) {
|
||||
free(inpcp);
|
||||
return NULL;
|
||||
}
|
||||
tmp = g_strdup_printf("%s/%sfoo", home, inpcp + 2);
|
||||
output_off = strlen(home) + 1;
|
||||
output_off = strlen(getenv("HOME")) + 1;
|
||||
} else {
|
||||
tmp = g_strdup_printf("%sfoo", inpcp);
|
||||
if (!tmp) {
|
||||
free(inpcp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(inpcp);
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
inpcp = tmp;
|
||||
|
||||
char* foofile = strdup(basename(tmp));
|
||||
char* directory = strdup(dirname(tmp));
|
||||
g_free(tmp);
|
||||
char* inpcp2 = strdup(inpcp);
|
||||
char* foofile = strdup(basename(inpcp2));
|
||||
char* directory = strdup(dirname(inpcp));
|
||||
free(inpcp);
|
||||
free(inpcp2);
|
||||
|
||||
GArray* files = g_array_new(TRUE, FALSE, sizeof(char*));
|
||||
g_array_set_clear_func(files, (GDestroyNotify)_filepath_item_free);
|
||||
@@ -1735,26 +1724,40 @@ cmd_ac_complete_filepath(const char* const input, char* const startstr, gboolean
|
||||
continue;
|
||||
}
|
||||
|
||||
char* acstring = NULL;
|
||||
char* acstring;
|
||||
if (output_off) {
|
||||
tmp = g_strdup_printf("%s/%s", directory, dir->d_name);
|
||||
if (tmp) {
|
||||
acstring = g_strdup_printf("~/%s", tmp + output_off);
|
||||
g_free(tmp);
|
||||
if (!tmp) {
|
||||
free(directory);
|
||||
free(foofile);
|
||||
return NULL;
|
||||
}
|
||||
acstring = g_strdup_printf("~/%s", tmp + output_off);
|
||||
if (!acstring) {
|
||||
free(directory);
|
||||
free(foofile);
|
||||
return NULL;
|
||||
}
|
||||
free(tmp);
|
||||
} else if (strcmp(directory, "/") == 0) {
|
||||
acstring = g_strdup_printf("/%s", dir->d_name);
|
||||
if (!acstring) {
|
||||
free(directory);
|
||||
free(foofile);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
acstring = g_strdup_printf("%s/%s", directory, dir->d_name);
|
||||
}
|
||||
if (!acstring) {
|
||||
g_array_free(files, TRUE);
|
||||
free(foofile);
|
||||
free(directory);
|
||||
return NULL;
|
||||
if (!acstring) {
|
||||
free(directory);
|
||||
free(foofile);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
g_array_append_val(files, acstring);
|
||||
char* acstring_cpy = strdup(acstring);
|
||||
g_array_append_val(files, acstring_cpy);
|
||||
free(acstring);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
@@ -4403,17 +4406,3 @@ _vcard_autocomplete(ProfWin* window, const char* const input, gboolean previous)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static char*
|
||||
_force_encryption_autocomplete(ProfWin* window, const char* const input, gboolean previous)
|
||||
{
|
||||
char* result = NULL;
|
||||
|
||||
result = autocomplete_param_with_ac(input, "/force-encryption policy", force_encryption_policy_ac, TRUE, previous);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = autocomplete_param_with_ac(input, "/force-encryption", force_encryption_ac, TRUE, previous);
|
||||
return result;
|
||||
}
|
||||
@@ -139,9 +139,8 @@ static const struct cmd_t command_defs[] = {
|
||||
"Show version and license information.")
|
||||
},
|
||||
|
||||
// Max args: account + server <s> + port <p> + tls <t> + auth <a> = 9
|
||||
{ CMD_PREAMBLE("/connect",
|
||||
parse_args, 0, 9, NULL)
|
||||
parse_args, 0, 7, NULL)
|
||||
CMD_MAINFUNC(cmd_connect)
|
||||
CMD_TAGS(
|
||||
CMD_TAG_CONNECTION)
|
||||
@@ -434,7 +433,7 @@ static const struct cmd_t command_defs[] = {
|
||||
{ "<contact>", "The contact you wish to view information about." },
|
||||
{ "<nick>", "When in a chat room, the occupant you wish to view information about." })
|
||||
CMD_EXAMPLES(
|
||||
"/info thor@asgard.server.org",
|
||||
"/info thor@aasgard.server.org",
|
||||
"/info heimdall")
|
||||
},
|
||||
|
||||
@@ -1903,9 +1902,9 @@ static const struct cmd_t command_defs[] = {
|
||||
CMD_ARGS(
|
||||
{ "where", "Show the current log file location." },
|
||||
{ "rotate on|off", "Rotate log, default on. Does not take effect if you specified a filename yourself when starting Profanity." },
|
||||
{ "maxsize <bytes>", "With rotate enabled, specifies the max log size, defaults to 10485760 (10MiB)." },
|
||||
{ "maxsize <bytes>", "With rotate enabled, specifies the max log size, defaults to 10485760 (10MB)." },
|
||||
{ "shared on|off", "Share logs between all instances, default: on. When off, the process id will be included in the log filename. Does not take effect if you specified a filename yourself when starting Profanity." },
|
||||
{ "level INFO|DEBUG|WARN|ERROR", "Set the log level. Default is INFO." })
|
||||
{"level INFO|DEBUG|WARN|ERROR", "Set the log level. Default is INFO. Only works with default log file, not with user provided log file during startup via -f." })
|
||||
},
|
||||
|
||||
{ CMD_PREAMBLE("/carbons",
|
||||
@@ -2730,7 +2729,6 @@ static const struct cmd_t command_defs[] = {
|
||||
{ "os on|off", "Choose whether to include the OS name if a user asks for software information (XEP-0092)." }
|
||||
)
|
||||
CMD_EXAMPLES(
|
||||
"/privacy",
|
||||
"/privacy logging off",
|
||||
"/privacy os off")
|
||||
},
|
||||
@@ -2746,31 +2744,6 @@ static const struct cmd_t command_defs[] = {
|
||||
"Redraw user interface. Can be used when some other program interrupted profanity or wrote to the same terminal and the interface looks \"broken\"." )
|
||||
},
|
||||
|
||||
{ CMD_PREAMBLE("/force-encryption",
|
||||
parse_args, 1, 3, &cons_encryption_setting)
|
||||
CMD_MAINFUNC(cmd_force_encryption)
|
||||
CMD_TAGS(
|
||||
CMD_TAG_CHAT)
|
||||
CMD_SYN(
|
||||
"/force-encryption",
|
||||
"/force-encryption on|off",
|
||||
"/force-encryption policy resend-to-confirm|block")
|
||||
CMD_DESC(
|
||||
"Configure forced encryption for outgoing messages in CProof. "
|
||||
"When enabled, restricts sending unencrypted messages based on the specified policy. "
|
||||
"Run without arguments to display current settings.")
|
||||
CMD_ARGS(
|
||||
{ "on|off", "Enable or disable forced encryption. When enabled, restricts unencrypted messages per the policy. When disabled, allows sending unencrypted messages without restrictions." },
|
||||
{ "policy resend-to-confirm|block", "Set the policy for unencrypted messages when forced encryption is enabled. 'resend-to-confirm' requires pressing Enter again to confirm sending an unencrypted message (default). 'block' prevents sending unencrypted messages entirely." }
|
||||
)
|
||||
CMD_EXAMPLES(
|
||||
"/force-encryption",
|
||||
"/force-encryption on",
|
||||
"/force-encryption off",
|
||||
"/force-encryption policy resend-to-confirm",
|
||||
"/force-encryption policy block")
|
||||
},
|
||||
|
||||
// NEXT-COMMAND (search helper)
|
||||
};
|
||||
|
||||
|
||||
@@ -1263,11 +1263,6 @@ cmd_sub(ProfWin* window, const char* const command, gchar** args)
|
||||
|
||||
auto_jid Jid* jidp = jid_create(jid);
|
||||
|
||||
if (jidp == NULL) {
|
||||
cons_show("Malformed JID: %s", jid);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (strcmp(subcmd, "allow") == 0) {
|
||||
presence_subscription(jidp->barejid, PRESENCE_SUBSCRIBED);
|
||||
cons_show("Accepted subscription for %s", jidp->barejid);
|
||||
@@ -3423,7 +3418,7 @@ cmd_caps(ProfWin* window, const char* const command, gchar** args)
|
||||
if (args[0]) {
|
||||
auto_jid Jid* jid = jid_create(args[0]);
|
||||
|
||||
if (jid == NULL || jid->fulljid == NULL) {
|
||||
if (jid->fulljid == NULL) {
|
||||
cons_show("You must provide a full jid to the /caps command.");
|
||||
} else {
|
||||
PContact pcontact = roster_get_contact(jid->barejid);
|
||||
@@ -5356,8 +5351,6 @@ cmd_time(ProfWin* window, const char* const command, gchar** args)
|
||||
cons_bad_cmd_usage(command);
|
||||
return TRUE;
|
||||
}
|
||||
if (!set_all)
|
||||
break;
|
||||
}
|
||||
if (!set_all && n == ARRAY_SIZE(time_prefs)) {
|
||||
cons_bad_cmd_usage(command);
|
||||
@@ -6273,11 +6266,10 @@ cmd_log(ProfWin* window, const char* const command, gchar** args)
|
||||
if (strcmp(subcmd, "level") == 0) {
|
||||
log_level_t prof_log_level;
|
||||
if (log_level_from_string(value, &prof_log_level) == 0) {
|
||||
auto_char char* log_file = strdup(get_log_file_location());
|
||||
log_close();
|
||||
log_init(prof_log_level, log_file);
|
||||
log_init(prof_log_level, NULL);
|
||||
|
||||
cons_show("Log level changed to: %s (log file: %s).", value, log_file ? log_file : "[default]");
|
||||
cons_show("Log level changed to: %s.", value);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -8580,20 +8572,19 @@ cmd_omemo_trust_mode(ProfWin* window, const char* const command, gchar** args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
|
||||
auto_gchar gchar* trust_mode = prefs_get_string(PREF_OMEMO_TRUST_MODE);
|
||||
if (!args[1]) {
|
||||
cons_show("Current trust mode is %s", trust_mode);
|
||||
cons_show("Current trust mode is %s", prefs_get_string(PREF_OMEMO_TRUST_MODE));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (g_strcmp0(args[1], "manual") == 0) {
|
||||
cons_show("Current trust mode is %s - setting to %s", trust_mode, args[1]);
|
||||
cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
|
||||
cons_show("You need to trust all OMEMO fingerprints manually");
|
||||
} else if (g_strcmp0(args[1], "firstusage") == 0) {
|
||||
cons_show("Current trust mode is %s - setting to %s", trust_mode, args[1]);
|
||||
cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
|
||||
cons_show("The first seen OMEMO fingerprints will be trusted automatically - new keys must be trusted manually");
|
||||
} else if (g_strcmp0(args[1], "blind") == 0) {
|
||||
cons_show("Current trust mode is %s - setting to %s", trust_mode, args[1]);
|
||||
cons_show("Current trust mode is %s - setting to %s", prefs_get_string(PREF_OMEMO_TRUST_MODE), args[1]);
|
||||
cons_show("ALL OMEMO fingerprints will be trusted automatically");
|
||||
} else {
|
||||
cons_bad_cmd_usage(command);
|
||||
@@ -9536,7 +9527,16 @@ cmd_change_password(ProfWin* window, const char* const command, gchar** args)
|
||||
gboolean
|
||||
cmd_editor(ProfWin* window, const char* const command, gchar** args)
|
||||
{
|
||||
get_message_from_editor_async(NULL);
|
||||
auto_gchar gchar* message = NULL;
|
||||
|
||||
if (get_message_from_editor(NULL, &message)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
rl_insert_text(message);
|
||||
ui_resize();
|
||||
rl_point = rl_end;
|
||||
rl_forced_update_display();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -10642,22 +10642,3 @@ cmd_vcard_save(ProfWin* window, const char* const command, gchar** args)
|
||||
cons_show("User vCard uploaded");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_force_encryption(ProfWin* window, const char* const command, gchar** args)
|
||||
{
|
||||
if (g_strv_length(args) == 1 && (g_strcmp0(args[0], "on") == 0 || g_strcmp0(args[0], "off") == 0)) {
|
||||
_cmd_set_boolean_preference(args[0], "Forces encryption", PREF_FORCE_ENCRYPTION);
|
||||
} else if (g_strv_length(args) == 2 && g_strcmp0(args[0], "policy") == 0) {
|
||||
if (g_strcmp0(args[1], "resend-to-confirm") != 0 && g_strcmp0(args[1], "block") != 0) {
|
||||
cons_show_error("Invalid policy: \"%s\". See '/help force-encryption' for details.", args[1]);
|
||||
return TRUE;
|
||||
}
|
||||
prefs_set_string(PREF_FORCE_ENCRYPTION_MODE, args[1]);
|
||||
cons_show("Force encryption policy has been set to \"%s\".", args[1]);
|
||||
} else {
|
||||
cons_bad_cmd_usage(command);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -269,6 +269,5 @@ gboolean cmd_vcard_photo(ProfWin* window, const char* const command, gchar** arg
|
||||
gboolean cmd_vcard_refresh(ProfWin* window, const char* const command, gchar** args);
|
||||
gboolean cmd_vcard_set(ProfWin* window, const char* const command, gchar** args);
|
||||
gboolean cmd_vcard_save(ProfWin* window, const char* const command, gchar** args);
|
||||
gboolean cmd_force_encryption(ProfWin* window, const char* const command, gchar** args);
|
||||
|
||||
#endif
|
||||
|
||||
19
src/common.c
19
src/common.c
@@ -45,7 +45,6 @@
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <curl/easy.h>
|
||||
@@ -200,10 +199,6 @@ load_custom_keyfile(prof_keyfile_t* keyfile, gchar* filename)
|
||||
|
||||
if (g_file_test(keyfile->filename, G_FILE_TEST_EXISTS)) {
|
||||
g_chmod(keyfile->filename, S_IRUSR | S_IWUSR);
|
||||
} else {
|
||||
int fno = g_creat(keyfile->filename, S_IRUSR | S_IWUSR);
|
||||
if (fno != -1)
|
||||
g_close(fno, NULL);
|
||||
}
|
||||
|
||||
return _load_keyfile(keyfile);
|
||||
@@ -660,17 +655,17 @@ call_external(gchar** argv)
|
||||
*
|
||||
* This function constructs an argument vector (argv) based on the provided template string, replacing placeholders ("%u" and "%p") with the provided URL and filename, respectively.
|
||||
*
|
||||
* @param template The template string with placeholders.
|
||||
* @param url The URL to replace "%u" (or NULL to skip).
|
||||
* @param filename The filename to replace "%p" (or NULL to skip).
|
||||
* @return The constructed argument vector (argv) as a null-terminated array of strings.
|
||||
* @param template_fmt The template string with placeholders.
|
||||
* @param url The URL to replace "%u" (or NULL to skip).
|
||||
* @param filename The filename to replace "%p" (or NULL to skip).
|
||||
* @return The constructed argument vector (argv) as a null-terminated array of strings.
|
||||
*
|
||||
* @note Remember to free the returned argument vector using `auto_gcharv` or `g_strfreev()`.
|
||||
*/
|
||||
gchar**
|
||||
format_call_external_argv(const char* template, const char* url, const char* filename)
|
||||
format_call_external_argv(const char* template_fmt, const char* url, const char* filename)
|
||||
{
|
||||
gchar** argv = g_strsplit(template, " ", 0);
|
||||
gchar** argv = g_strsplit(template_fmt, " ", 0);
|
||||
|
||||
guint num_args = 0;
|
||||
while (argv[num_args]) {
|
||||
@@ -679,7 +674,7 @@ format_call_external_argv(const char* template, const char* url, const char* fil
|
||||
argv[num_args] = g_strdup(url);
|
||||
} else if (0 == g_strcmp0(argv[num_args], "%p") && filename != NULL) {
|
||||
g_free(argv[num_args]);
|
||||
argv[num_args] = strdup(filename);
|
||||
argv[num_args] = g_strdup(filename);
|
||||
}
|
||||
num_args++;
|
||||
}
|
||||
|
||||
@@ -171,8 +171,6 @@ typedef enum {
|
||||
RESOURCE_XA
|
||||
} resource_presence_t;
|
||||
|
||||
extern gboolean background_mode;
|
||||
|
||||
gboolean string_to_verbosity(const char* cmd, int* verbosity, gchar** err_msg);
|
||||
|
||||
gboolean create_dir(const char* name);
|
||||
@@ -198,7 +196,7 @@ void get_file_paths_recursive(const char* directory, GSList** contents);
|
||||
char* get_random_string(int length);
|
||||
|
||||
gboolean call_external(gchar** argv);
|
||||
gchar** format_call_external_argv(const char* template, const char* url, const char* filename);
|
||||
gchar** format_call_external_argv(const char* template_fmt, const char* url, const char* filename);
|
||||
|
||||
gchar* unique_filename_from_url(const char* url, const char* path);
|
||||
gchar* get_expanded_path(const char* path);
|
||||
|
||||
@@ -1792,8 +1792,6 @@ _get_group(preference_t pref)
|
||||
case PREF_OUTGOING_STAMP:
|
||||
case PREF_INCOMING_STAMP:
|
||||
case PREF_MOOD:
|
||||
case PREF_FORCE_ENCRYPTION:
|
||||
case PREF_FORCE_ENCRYPTION_MODE:
|
||||
return PREF_GROUP_UI;
|
||||
case PREF_STATES:
|
||||
case PREF_OUTTYPE:
|
||||
@@ -2152,10 +2150,6 @@ _get_key(preference_t pref)
|
||||
return "strophe.sm.enabled";
|
||||
case PREF_STROPHE_SM_RESEND:
|
||||
return "strophe.sm.resend";
|
||||
case PREF_FORCE_ENCRYPTION:
|
||||
return "force-encryption.enabled";
|
||||
case PREF_FORCE_ENCRYPTION_MODE:
|
||||
return "force-encryption.policy";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@@ -2210,7 +2204,6 @@ _get_default_boolean(preference_t pref)
|
||||
case PREF_STROPHE_SM_RESEND:
|
||||
return TRUE;
|
||||
case PREF_PGP_PUBKEY_AUTOIMPORT:
|
||||
case PREF_FORCE_ENCRYPTION:
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
@@ -2317,8 +2310,6 @@ _get_default_string(preference_t pref)
|
||||
return "0";
|
||||
case PREF_DBLOG:
|
||||
return "on";
|
||||
case PREF_FORCE_ENCRYPTION_MODE:
|
||||
return "resend-to-confirm";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -187,8 +187,6 @@ typedef enum {
|
||||
PREF_STROPHE_SM_RESEND,
|
||||
PREF_VCARD_PHOTO_CMD,
|
||||
PREF_STATUSBAR_TABMODE,
|
||||
PREF_FORCE_ENCRYPTION,
|
||||
PREF_FORCE_ENCRYPTION_MODE
|
||||
} preference_t;
|
||||
|
||||
typedef struct prof_alias_t
|
||||
|
||||
@@ -42,11 +42,9 @@
|
||||
#include "log.h"
|
||||
#include "chatlog.h"
|
||||
#include "database.h"
|
||||
#include "client_events.h"
|
||||
#include "config/preferences.h"
|
||||
#include "event/common.h"
|
||||
#include "plugins/plugins.h"
|
||||
#include "ui/inputwin.h"
|
||||
#include "ui/window_list.h"
|
||||
#include "xmpp/chat_session.h"
|
||||
#include "xmpp/session.h"
|
||||
@@ -179,7 +177,7 @@ cl_ev_send_msg_correct(ProfChatWin* chatwin, const char* const msg, const char*
|
||||
#ifdef HAVE_LIBOTR
|
||||
handled = otr_on_message_send(chatwin, message, request_receipt, replace_id);
|
||||
#endif
|
||||
if (!handled && allow_unencrypted_message(chatwin, message)) {
|
||||
if (!handled) {
|
||||
auto_char char* id = message_send_chat(chatwin->barejid, message, oob_url, request_receipt, replace_id);
|
||||
chat_log_msg_out(chatwin->barejid, message, NULL);
|
||||
log_database_add_outgoing_chat(id, chatwin->barejid, message, replace_id, PROF_MSG_ENC_NONE);
|
||||
@@ -253,34 +251,3 @@ cl_ev_send_priv_msg(ProfPrivateWin* privwin, const char* const msg, const char*
|
||||
plugins_post_priv_message_send(privwin->fulljid, message);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
allow_unencrypted_message(ProfChatWin* chatwin, const char* const msg)
|
||||
{
|
||||
if (!prefs_get_boolean(PREF_FORCE_ENCRYPTION)) {
|
||||
return TRUE; // Encryption not enforced
|
||||
}
|
||||
|
||||
auto_gchar gchar* force_enc_mode = prefs_get_string(PREF_FORCE_ENCRYPTION_MODE);
|
||||
|
||||
if (g_strcmp0(force_enc_mode, "block") == 0) {
|
||||
win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: encryption required. Enable (omemo/pgp/otr) encryption or /force-encryption off.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_strcmp0(force_enc_mode, "resend-to-confirm") == 0) {
|
||||
// We do not use chatwin->last_message because it should be set only if it's sent.
|
||||
guint msg_hash = g_str_hash(msg);
|
||||
if (msg_hash == chatwin->last_attempted_msg_hash) {
|
||||
chatwin->last_attempted_msg_hash = 0; // reset hash
|
||||
return TRUE;
|
||||
}
|
||||
win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: encryption required. Enable (omemo/pgp/otr) encryption or press Enter again to send without encryption. Use /force-encryption off to disable this protection.");
|
||||
chatwin->last_attempted_msg_hash = msg_hash;
|
||||
inp_set_line(msg); // reset message to prevent the need to retype it
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
win_println((ProfWin*)chatwin, THEME_ERROR, "-", "Message not sent: invalid encryption mode (%s). Use '/force-encryption policy resend-to-confirm'.", force_enc_mode);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,4 @@ void cl_ev_send_muc_msg_corrected(ProfMucWin* mucwin, const char* const msg, con
|
||||
void cl_ev_send_muc_msg(ProfMucWin* mucwin, const char* const msg, const char* const oob_url);
|
||||
void cl_ev_send_priv_msg(ProfPrivateWin* privwin, const char* const msg, const char* const oob_url);
|
||||
|
||||
// Checks if an unencrypted message can be sent based on encryption preferences.
|
||||
// Returns TRUE if allowed, FALSE if blocked.
|
||||
gboolean allow_unencrypted_message(ProfChatWin* chatwin, const char* const msg);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#ifndef EVENT_COMMON_H
|
||||
#define EVENT_COMMON_H
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
void ev_disconnect_cleanup(void);
|
||||
void ev_inc_connection_counter(void);
|
||||
void ev_reset_connection_counter(void);
|
||||
|
||||
@@ -186,7 +186,7 @@ log_error(const char* const msg, ...)
|
||||
}
|
||||
|
||||
void
|
||||
log_init(log_level_t filter, const char* const log_file)
|
||||
log_init(log_level_t filter, char* log_file)
|
||||
{
|
||||
level_filter = filter;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ typedef enum {
|
||||
PROF_LEVEL_ERROR
|
||||
} log_level_t;
|
||||
|
||||
void log_init(log_level_t filter, const char* const log_file);
|
||||
void log_init(log_level_t filter, char* log_file);
|
||||
log_level_t log_get_filter(void);
|
||||
void log_close(void);
|
||||
const gchar* get_log_file_location(void);
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#ifndef PGP_GPG_H
|
||||
#define PGP_GPG_H
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
typedef struct pgp_key_t
|
||||
{
|
||||
char* id;
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
#include "plugins/settings.h"
|
||||
#include "plugins/disco.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/win_types.h"
|
||||
#include "ui/window_list.h"
|
||||
#include "xmpp/roster_list.h"
|
||||
|
||||
@@ -206,13 +205,6 @@ api_get_current_recipient(void)
|
||||
}
|
||||
}
|
||||
|
||||
gchar*
|
||||
api_get_current_window(void)
|
||||
{
|
||||
ProfWin* current = wins_get_current();
|
||||
return win_get_title(current);
|
||||
}
|
||||
|
||||
char*
|
||||
api_get_current_muc(void)
|
||||
{
|
||||
|
||||
@@ -46,7 +46,6 @@ void api_notify(const char* message, const char* category, int timeout_ms);
|
||||
void api_send_line(char* line);
|
||||
|
||||
char* api_get_current_recipient(void);
|
||||
gchar* api_get_current_window(void);
|
||||
char* api_get_current_muc(void);
|
||||
gboolean api_current_win_is_console(void);
|
||||
char* api_get_current_nick(void);
|
||||
|
||||
@@ -165,12 +165,6 @@ c_api_get_current_recipient(void)
|
||||
return api_get_current_recipient();
|
||||
}
|
||||
|
||||
static char*
|
||||
c_api_get_current_window(void)
|
||||
{
|
||||
return api_get_current_window();
|
||||
}
|
||||
|
||||
static char*
|
||||
c_api_get_current_muc(void)
|
||||
{
|
||||
@@ -483,7 +477,6 @@ c_api_init(void)
|
||||
prof_notify = c_api_notify;
|
||||
prof_send_line = c_api_send_line;
|
||||
prof_get_current_recipient = c_api_get_current_recipient;
|
||||
prof_get_current_window = c_api_get_current_window;
|
||||
prof_get_current_muc = c_api_get_current_muc;
|
||||
prof_current_win_is_console = c_api_current_win_is_console;
|
||||
prof_get_current_nick = c_api_get_current_nick;
|
||||
|
||||
@@ -62,7 +62,6 @@ void (*prof_notify)(const char* message, int timeout_ms, const char* category) =
|
||||
void (*prof_send_line)(char* line) = NULL;
|
||||
|
||||
char* (*prof_get_current_recipient)(void) = NULL;
|
||||
char* (*prof_get_current_window)(void) = NULL;
|
||||
char* (*prof_get_current_muc)(void) = NULL;
|
||||
int (*prof_current_win_is_console)(void) = NULL;
|
||||
char* (*prof_get_current_nick)(void) = NULL;
|
||||
|
||||
@@ -71,7 +71,6 @@ void (*prof_notify)(const char* message, int timeout_ms, const char* category);
|
||||
void (*prof_send_line)(char* line);
|
||||
|
||||
char* (*prof_get_current_recipient)(void);
|
||||
char* (*prof_get_current_window)(void);
|
||||
char* (*prof_get_current_muc)(void);
|
||||
int (*prof_current_win_is_console)(void);
|
||||
char* (*prof_get_current_nick)(void);
|
||||
|
||||
@@ -415,15 +415,6 @@ python_api_get_current_recipient(PyObject* self, PyObject* args)
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
python_api_get_current_window(PyObject* self, PyObject* args)
|
||||
{
|
||||
allow_python_threads();
|
||||
auto_gchar gchar* recipient = api_get_current_window();
|
||||
disable_python_threads();
|
||||
return Py_BuildValue("s", recipient);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
python_api_get_current_muc(PyObject* self, PyObject* args)
|
||||
{
|
||||
@@ -1539,7 +1530,6 @@ static PyMethodDef apiMethods[] = {
|
||||
{ "send_line", python_api_send_line, METH_VARARGS, "Send a line of input." },
|
||||
{ "notify", python_api_notify, METH_VARARGS, "Send desktop notification." },
|
||||
{ "get_current_recipient", python_api_get_current_recipient, METH_VARARGS, "Return the jid of the recipient of the current window." },
|
||||
{ "get_current_window", python_api_get_current_window, METH_VARARGS, "Return title of the current window." },
|
||||
{ "get_current_muc", python_api_get_current_muc, METH_VARARGS, "Return the jid of the room of the current window." },
|
||||
{ "get_current_nick", python_api_get_current_nick, METH_VARARGS, "Return nickname in current room." },
|
||||
{ "get_name_from_roster", python_api_get_name_from_roster, METH_VARARGS, "Return nickname in roster of barejid." },
|
||||
|
||||
@@ -86,16 +86,10 @@ python_get_version_number(void)
|
||||
return version_number;
|
||||
}
|
||||
|
||||
static void
|
||||
_unref_module(PyObject* module)
|
||||
{
|
||||
Py_XDECREF(module);
|
||||
}
|
||||
|
||||
void
|
||||
python_env_init(void)
|
||||
{
|
||||
loaded_modules = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_unref_module);
|
||||
loaded_modules = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)Py_XDECREF);
|
||||
|
||||
python_init_prof();
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
#include "config/tlscerts.h"
|
||||
#include "config/scripts.h"
|
||||
#include "command/cmd_defs.h"
|
||||
#include "tools/editor.h"
|
||||
#include "plugins/plugins.h"
|
||||
#include "event/client_events.h"
|
||||
#include "ui/ui.h"
|
||||
@@ -94,9 +93,6 @@ static void _connect_default(const char* const account);
|
||||
pthread_mutex_t lock;
|
||||
static gboolean force_quit = FALSE;
|
||||
|
||||
// main.c (prof_run section)
|
||||
gboolean background_mode = FALSE;
|
||||
|
||||
void
|
||||
prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_file, gchar* theme_name, gchar** commands)
|
||||
{
|
||||
@@ -122,44 +118,42 @@ prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_f
|
||||
log_stderr_handler();
|
||||
session_check_autoaway();
|
||||
|
||||
if (!background_mode) {
|
||||
line = commands ? *commands : inp_readline();
|
||||
if (commands && line && memcmp(line, "/sleep", 6) == 0) {
|
||||
if (!g_timer_is_active(waittimer)) {
|
||||
gchar* err_msg;
|
||||
if (strtoi_range(line + 7, &waittime, 0, 300, &err_msg)) {
|
||||
g_timer_start(waittimer);
|
||||
/* Increase the minimal runtime by the waiting time
|
||||
* so we can be sure there's runtime left after executing
|
||||
* the last command.
|
||||
*/
|
||||
min_runtime += waittime;
|
||||
} else {
|
||||
log_error(err_msg);
|
||||
g_free(err_msg);
|
||||
commands = NULL;
|
||||
}
|
||||
} else if (g_timer_elapsed(waittimer, NULL) >= waittime) {
|
||||
g_timer_stop(waittimer);
|
||||
commands++;
|
||||
if (!(*commands))
|
||||
commands = NULL;
|
||||
}
|
||||
cont = TRUE;
|
||||
} else if (line) {
|
||||
ProfWin* window = wins_get_current();
|
||||
cont = cmd_process_input(window, line);
|
||||
if (commands) {
|
||||
commands++;
|
||||
if (!(*commands))
|
||||
commands = NULL;
|
||||
line = commands ? *commands : inp_readline();
|
||||
if (commands && line && memcmp(line, "/sleep", 6) == 0) {
|
||||
if (!g_timer_is_active(waittimer)) {
|
||||
gchar* err_msg;
|
||||
if (strtoi_range(line + 7, &waittime, 0, 300, &err_msg)) {
|
||||
g_timer_start(waittimer);
|
||||
/* Increase the minimal runtime by the waiting time
|
||||
* so we can be sure there's runtime left after executing
|
||||
* the last command.
|
||||
*/
|
||||
min_runtime += waittime;
|
||||
} else {
|
||||
free(line);
|
||||
line = NULL;
|
||||
log_error(err_msg);
|
||||
g_free(err_msg);
|
||||
commands = NULL;
|
||||
}
|
||||
} else {
|
||||
cont = TRUE;
|
||||
} else if (g_timer_elapsed(waittimer, NULL) >= waittime) {
|
||||
g_timer_stop(waittimer);
|
||||
commands++;
|
||||
if (!(*commands))
|
||||
commands = NULL;
|
||||
}
|
||||
cont = TRUE;
|
||||
} else if (line) {
|
||||
ProfWin* window = wins_get_current();
|
||||
cont = cmd_process_input(window, line);
|
||||
if (commands) {
|
||||
commands++;
|
||||
if (!(*commands))
|
||||
commands = NULL;
|
||||
} else {
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
} else {
|
||||
cont = TRUE;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBOTR
|
||||
@@ -169,12 +163,7 @@ prof_run(gchar* log_level, gchar* account_name, gchar* config_file, gchar* log_f
|
||||
notify_remind();
|
||||
session_process_events();
|
||||
iq_autoping_check();
|
||||
|
||||
editor_process(wins_get_current());
|
||||
|
||||
if (!background_mode) {
|
||||
ui_update();
|
||||
}
|
||||
ui_update();
|
||||
#ifdef HAVE_GTK
|
||||
tray_update();
|
||||
#endif
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
#ifndef BOOKMARK_IGNORE_H
|
||||
#define BOOKMARK_IGNORE_H
|
||||
|
||||
void bookmark_ignore_on_connect();
|
||||
#include "xmpp/xmpp.h"
|
||||
|
||||
void bookmark_ignore_on_connect(const char* const barejid);
|
||||
void bookmark_ignore_on_disconnect();
|
||||
gboolean bookmark_ignored(Bookmark* bookmark);
|
||||
gchar** bookmark_ignore_list(gsize* len);
|
||||
|
||||
@@ -38,200 +38,15 @@
|
||||
#include <fcntl.h>
|
||||
#include <glib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h> // necessary for readline
|
||||
#include <readline/readline.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "config/files.h"
|
||||
#include "config/preferences.h"
|
||||
#include "editor.h"
|
||||
#include "log.h"
|
||||
#include "common.h"
|
||||
#include "ncurses.h"
|
||||
#include "ui/ui.h"
|
||||
#include "xmpp/xmpp.h"
|
||||
|
||||
EditorTask editor_task = { 0, FALSE, NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL };
|
||||
|
||||
void*
|
||||
editor_thread(void* arg)
|
||||
{
|
||||
EditorTask* task = (EditorTask*)arg;
|
||||
auto_gcharv gchar** argv = g_strsplit(task->editor_cmd, " ", 0);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
execvp(argv[0], argv);
|
||||
log_error("[Editor] Failed to exec %s", argv[0]);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&task->mutex);
|
||||
if (pid > 0) {
|
||||
task->pid = pid;
|
||||
int status;
|
||||
// Unlock mutex to avoid deadlock on the main loop
|
||||
pthread_mutex_unlock(&task->mutex);
|
||||
waitpid(pid, &status, 0);
|
||||
pthread_mutex_lock(&task->mutex);
|
||||
|
||||
gsize length;
|
||||
if (!g_file_get_contents(task->filename, &task->result, &length, &task->error)) {
|
||||
log_error("[Editor] could not read from %s: %s", task->filename,
|
||||
task->error ? task->error->message : "No GLib error given");
|
||||
task->result = g_strdup(""); // Ensure valid result
|
||||
} else {
|
||||
g_strchomp(task->result);
|
||||
if (remove(task->filename) != 0) {
|
||||
log_error("[Editor] error during file deletion of %s", task->filename);
|
||||
} else {
|
||||
log_debug("[Editor] deleted file: %s", task->filename);
|
||||
}
|
||||
}
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
task->error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Editor process failed with status %d", WEXITSTATUS(status));
|
||||
}
|
||||
} else {
|
||||
task->error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED, "Fork failed: %s", strerror(errno));
|
||||
task->result = g_strdup(""); // Ensure valid result
|
||||
}
|
||||
|
||||
task->active = FALSE;
|
||||
pthread_mutex_unlock(&task->mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns true if an error occurred
|
||||
gboolean
|
||||
get_message_from_editor_async(gchar* message)
|
||||
{
|
||||
// Check if editor is already running
|
||||
pthread_mutex_lock(&editor_task.mutex);
|
||||
if (editor_task.active) {
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
log_error("[Editor] Editor already running");
|
||||
return TRUE;
|
||||
}
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
|
||||
// Create temporary file
|
||||
auto_gchar gchar* filename = NULL;
|
||||
GError* glib_error = NULL;
|
||||
const char* jid = connection_get_barejid();
|
||||
if (jid) {
|
||||
filename = files_file_in_account_data_path(DIR_EDITOR, jid, "compose.md");
|
||||
} else {
|
||||
log_debug("[Editor] could not get JID");
|
||||
auto_gchar gchar* data_dir = files_get_data_path(DIR_EDITOR);
|
||||
if (!create_dir(data_dir)) {
|
||||
log_error("[Editor] could not create directory %s", data_dir);
|
||||
return TRUE;
|
||||
}
|
||||
filename = g_strdup_printf("%s/compose.md", data_dir);
|
||||
}
|
||||
if (!filename) {
|
||||
log_error("[Editor] something went wrong while creating compose file");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Write message to file
|
||||
gsize messagelen = message ? strlen(message) : 0;
|
||||
if (!g_file_set_contents(filename, message ? message : "", messagelen, &glib_error)) {
|
||||
log_error("[Editor] could not write to %s: %s", filename,
|
||||
glib_error ? glib_error->message : "No GLib error given");
|
||||
if (glib_error) {
|
||||
g_error_free(glib_error);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Prepare editor command
|
||||
auto_gchar gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
|
||||
auto_gchar gchar* editor_with_filename = g_strdup_printf("%s %s", editor, filename);
|
||||
|
||||
// Suspend NCurses
|
||||
def_prog_mode();
|
||||
endwin();
|
||||
|
||||
// Initialize thread task
|
||||
pthread_mutex_lock(&editor_task.mutex);
|
||||
editor_task.active = TRUE;
|
||||
editor_task.result = NULL;
|
||||
editor_task.error = NULL;
|
||||
g_free(editor_task.filename);
|
||||
g_free(editor_task.editor_cmd);
|
||||
editor_task.filename = g_strdup(filename);
|
||||
editor_task.editor_cmd = g_strdup(editor_with_filename);
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
|
||||
// Set background mode
|
||||
background_mode = TRUE;
|
||||
log_debug("[Editor] Entering background mode");
|
||||
|
||||
// Start editor thread
|
||||
if (pthread_create(&editor_task.thread, NULL, editor_thread, &editor_task) != 0) {
|
||||
pthread_mutex_lock(&editor_task.mutex);
|
||||
editor_task.active = FALSE;
|
||||
editor_task.error = g_error_new(G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||||
"Failed to create editor thread: %s", strerror(errno));
|
||||
g_free(editor_task.filename);
|
||||
g_free(editor_task.editor_cmd);
|
||||
editor_task.filename = NULL;
|
||||
editor_task.editor_cmd = NULL;
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
background_mode = FALSE;
|
||||
log_debug("[Editor] Exiting background mode");
|
||||
reset_prog_mode();
|
||||
doupdate();
|
||||
log_error("[Editor] Failed to create editor thread");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
editor_process(ProfWin* window)
|
||||
{
|
||||
pthread_mutex_lock(&editor_task.mutex);
|
||||
if (editor_task.active) {
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
return;
|
||||
}
|
||||
if (!editor_task.result) {
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
g_strchomp(editor_task.result);
|
||||
rl_replace_line(editor_task.result, 0);
|
||||
rl_point = rl_end;
|
||||
rl_forced_update_display();
|
||||
reset_prog_mode();
|
||||
doupdate();
|
||||
ui_resize();
|
||||
g_free(editor_task.result);
|
||||
g_free(editor_task.filename);
|
||||
g_free(editor_task.editor_cmd);
|
||||
editor_task.result = NULL;
|
||||
editor_task.filename = NULL;
|
||||
editor_task.editor_cmd = NULL;
|
||||
if (editor_task.error) {
|
||||
log_error("[Editor] %s", editor_task.error->message);
|
||||
g_error_free(editor_task.error);
|
||||
editor_task.error = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&editor_task.mutex);
|
||||
background_mode = FALSE;
|
||||
log_debug("[Editor] Exiting background mode");
|
||||
}
|
||||
|
||||
// Deprecated synchronous editor call. Returns a message as returned_message.
|
||||
// Please avoid using it, since it blocks execution of the message handling and other important functionality until the editor is closed.
|
||||
// Returns true if an error occurred
|
||||
gboolean
|
||||
get_message_from_editor(gchar* message, gchar** returned_message)
|
||||
|
||||
@@ -37,24 +37,8 @@
|
||||
#ifndef TOOLS_EDITOR_H
|
||||
#define TOOLS_EDITOR_H
|
||||
|
||||
#include "ui/win_types.h"
|
||||
#include <glib.h>
|
||||
typedef struct
|
||||
{
|
||||
pid_t pid; // Editor process PID
|
||||
gboolean active; // Thread running
|
||||
gchar* result; // Edited message
|
||||
GError* error; // Error if any
|
||||
pthread_mutex_t mutex; // Protect access
|
||||
pthread_t thread; // Editor thread
|
||||
gchar* filename; // Temporary file path
|
||||
gchar* editor_cmd; // Editor command with filename
|
||||
} EditorTask;
|
||||
|
||||
extern EditorTask editor_task;
|
||||
|
||||
gboolean get_message_from_editor(gchar* message, gchar** returned_message);
|
||||
gboolean get_message_from_editor_async(gchar* message);
|
||||
void editor_process(ProfWin* window);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
/*
|
||||
* buffer.c
|
||||
*
|
||||
* Message buffer implementation for CProof.
|
||||
*
|
||||
* This module provides an in-memory buffer for managing active chat entries in the
|
||||
* console-based XMPP client. Separate from persistent SQLite storage, it holds
|
||||
* messages and metadata solely for UI rendering, ensuring responsive scrolling and
|
||||
* display without exceeding ncurses pad limits (10k lines). By tracking rendered
|
||||
* line counts per entry, it proactively trims content to prevent overflow, enabling
|
||||
* seamless integration with ncurses for position-aware rendering.
|
||||
*
|
||||
* Key features and operations:
|
||||
* - Append/prepend entries (messages) with automatic line-based cleanup.
|
||||
* - Track cumulative lines for overflow prevention and efficient buffer sizing.
|
||||
* - Remove entries by ID or index; mark delivery receipts as received.
|
||||
* - Retrieve entries by index or ID for rendering and history queries.
|
||||
* - Store metadata including timestamps, senders, themes, and ncurses y-positions.
|
||||
*
|
||||
* CProof. Fork of Profanity (since 2025).
|
||||
* vim: expandtab:ts=4:sts=4:sw=4
|
||||
*
|
||||
* Copyright (C) 2012 - 2019 James Booth <boothj5@gmail.com>
|
||||
* Copyright (C) 2019 - 2025 Michael Vetter <jubalh@iodoru.org>
|
||||
* Copyright (C) 2025 CProof Developers
|
||||
*
|
||||
* Licensed under the GNU General Public License, version 3,
|
||||
* with the OpenSSL exception. See LICENSE for details.
|
||||
* This file is part of Profanity.
|
||||
*
|
||||
* Profanity is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Profanity 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Profanity. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link the code of portions of this program with the OpenSSL library under
|
||||
* certain conditions as described in each individual source file, and
|
||||
* distribute linked combinations including the two.
|
||||
*
|
||||
* You must obey the GNU General Public License in all respects for all of the
|
||||
* code used other than OpenSSL. If you modify file(s) with this exception, you
|
||||
* may extend this exception to your version of the file(s), but you are not
|
||||
* obligated to do so. If you do not wish to do so, delete this exception
|
||||
* statement from your version. If you delete this exception statement from all
|
||||
* source files in the program, then also delete it here.
|
||||
*
|
||||
* vim: expandtab:ts=4:sts=4:sw=4
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ui/window_list.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -50,8 +54,15 @@
|
||||
#include "ui/window.h"
|
||||
#include "ui/buffer.h"
|
||||
|
||||
#define MAX_BUFFER_SIZE 200
|
||||
#define STRDUP_OR_NULL(str) ((str) ? strdup(str) : NULL)
|
||||
|
||||
struct prof_buff_t
|
||||
{
|
||||
GSList* entries;
|
||||
int lines;
|
||||
};
|
||||
|
||||
static void _free_entry(ProfBuffEntry* entry);
|
||||
static ProfBuffEntry* _create_entry(const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos);
|
||||
static void _buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* time, int flags, theme_item_t theme_item, const char* const display_from, const char* const from_jid, const char* const message, DeliveryReceipt* receipt, const char* const id, int y_start_pos, int y_end_pos, gboolean append);
|
||||
@@ -97,24 +108,23 @@ _buffer_add(ProfBuff buffer, const char* show_char, int pad_indent, GDateTime* t
|
||||
|
||||
buffer->lines += e->_lines;
|
||||
|
||||
// With 10% margin to ensure overflow prevention
|
||||
while (buffer->lines >= PAD_SIZE - (PAD_SIZE / 10) && g_slist_length(buffer->entries) > 1) {
|
||||
while (g_slist_length(buffer->entries) >= MAX_BUFFER_SIZE) {
|
||||
// Delete message from the opposite size to free buffer
|
||||
GSList* buffer_entry_to_delete = append ? buffer->entries : g_slist_last(buffer->entries);
|
||||
ProfBuffEntry* entry_to_delete = (ProfBuffEntry*)buffer_entry_to_delete->data;
|
||||
// log_debug("(Messages left in buffer: %d) DELETING: %s", g_slist_length(buffer->entries), entry_to_delete->id);
|
||||
buffer->lines -= entry_to_delete->_lines;
|
||||
_free_entry(entry_to_delete);
|
||||
buffer->entries = g_slist_delete_link(buffer->entries, buffer_entry_to_delete);
|
||||
}
|
||||
|
||||
if (from_jid && y_end_pos == y_start_pos) {
|
||||
// win_redraw will redraw with entries above removed,
|
||||
// it will also recalculate actual size of the message that caused overflow
|
||||
int old_lines = buffer->lines;
|
||||
win_redraw(wins_get_current());
|
||||
log_debug("Ncurses Overflow! From: %s, position: %d, ID: %s, append: %s, used message buffer size: %d, rendered lines: (old: %d/ actual: %d/ max: %d)",
|
||||
from_jid, y_start_pos, id, append ? "TRUE" : "FALSE", g_slist_length(buffer->entries), old_lines, buffer->lines, PAD_SIZE);
|
||||
log_warning("Ncurses Overflow! From: %s, position: %d, ID: %s, append: %s, used message buffer size: %d, message buffer size: %d, rendered lines buffer size: %d",
|
||||
from_jid, y_start_pos, id, append ? "TRUE" : "FALSE", g_slist_length(buffer->entries), MAX_BUFFER_SIZE, PAD_SIZE);
|
||||
// At this point we have a message that caused overflow of the render buffer.
|
||||
// Ideally, we want to clean other messages to rerender everything properly. To do so,
|
||||
// first we need to determine the size of the message that we are trying to display,
|
||||
// then we need to print the message.
|
||||
// However, _buffer_add is too late in the code to do this, therefore it has to be done in the win_print_old_history.
|
||||
}
|
||||
|
||||
buffer->entries = append ? g_slist_append(buffer->entries, e) : g_slist_prepend(buffer->entries, e);
|
||||
|
||||
@@ -68,12 +68,6 @@ typedef struct prof_buff_entry_t
|
||||
char* id;
|
||||
} ProfBuffEntry;
|
||||
|
||||
struct prof_buff_t
|
||||
{
|
||||
GSList* entries;
|
||||
int lines;
|
||||
};
|
||||
|
||||
typedef struct prof_buff_t* ProfBuff;
|
||||
|
||||
ProfBuff buffer_create();
|
||||
|
||||
@@ -424,11 +424,9 @@ chatwin_outgoing_msg(ProfChatWin* chatwin, const char* const message, const char
|
||||
win_print_outgoing((ProfWin*)chatwin, enc_char, id, replace_id, display_message);
|
||||
}
|
||||
|
||||
// Save last id and message for LMC
|
||||
// Note: if the same message is to be corrected several times, the id of the original message is used in each case
|
||||
// https://xmpp.org/extensions/xep-0308.html#rules
|
||||
if (id) {
|
||||
_chatwin_set_last_message(chatwin, replace_id ?: id, display_message);
|
||||
// save last id and message for LMC in case if it's not LMC message
|
||||
if (id && !replace_id) {
|
||||
_chatwin_set_last_message(chatwin, id, display_message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,13 +625,9 @@ chatwin_db_history(ProfChatWin* chatwin, const gchar* start_time, const gchar* e
|
||||
static void
|
||||
_chatwin_set_last_message(ProfChatWin* chatwin, const char* const id, const char* const message)
|
||||
{
|
||||
if (message && chatwin->last_message != message) {
|
||||
free(chatwin->last_message);
|
||||
chatwin->last_message = strdup(message);
|
||||
}
|
||||
free(chatwin->last_message);
|
||||
chatwin->last_message = strdup(message);
|
||||
|
||||
if (id && chatwin->last_msg_id != id) {
|
||||
free(chatwin->last_msg_id);
|
||||
chatwin->last_msg_id = strdup(id);
|
||||
}
|
||||
free(chatwin->last_msg_id);
|
||||
chatwin->last_msg_id = strdup(id);
|
||||
}
|
||||
|
||||
@@ -2891,11 +2891,3 @@ cons_privacy_setting(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cons_encryption_setting(void)
|
||||
{
|
||||
cons_show("Force encryption : %sabled", prefs_get_boolean(PREF_FORCE_ENCRYPTION) ? "en" : "dis");
|
||||
|
||||
cons_show("Force encryption mode : %s", prefs_get_string(PREF_FORCE_ENCRYPTION_MODE));
|
||||
}
|
||||
|
||||
@@ -170,29 +170,6 @@ create_input_window(void)
|
||||
_inp_win_update_virtual();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_inp_slashguard_check(void)
|
||||
{
|
||||
if (get_password)
|
||||
return false;
|
||||
/* ignore empty and quoted messages */
|
||||
if (inp_line == NULL || inp_line[0] == '\0' || inp_line[0] == '>')
|
||||
return false;
|
||||
if (!prefs_get_boolean(PREF_SLASH_GUARD))
|
||||
return false;
|
||||
size_t n = 1;
|
||||
while (inp_line[n] != '\0' && n < 4) {
|
||||
if (inp_line[n] == '/') {
|
||||
cons_show("Your text contains a slash in the first 4 characters");
|
||||
free(inp_line);
|
||||
inp_line = NULL;
|
||||
return true;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char*
|
||||
inp_readline(void)
|
||||
{
|
||||
@@ -226,7 +203,17 @@ inp_readline(void)
|
||||
chat_state_idle();
|
||||
}
|
||||
|
||||
if (inp_line && !_inp_slashguard_check()) {
|
||||
if (inp_line) {
|
||||
if (!get_password && prefs_get_boolean(PREF_SLASH_GUARD)) {
|
||||
// ignore quoted messages
|
||||
if (strlen(inp_line) > 1 && inp_line[0] != '>') {
|
||||
char* res = (char*)memchr(inp_line + 1, '/', 3);
|
||||
if (res) {
|
||||
cons_show("Your text contains a slash in the first 4 characters");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
char* ret = inp_line;
|
||||
inp_line = NULL;
|
||||
return ret;
|
||||
@@ -307,14 +294,6 @@ inp_get_line(void)
|
||||
return line;
|
||||
}
|
||||
|
||||
void
|
||||
inp_set_line(const char* const new_line)
|
||||
{
|
||||
rl_replace_line(new_line, 1);
|
||||
rl_point = rl_end;
|
||||
_inp_redisplay();
|
||||
}
|
||||
|
||||
char*
|
||||
inp_get_password(void)
|
||||
{
|
||||
@@ -995,7 +974,14 @@ _inp_rl_send_to_editor(int count, int key)
|
||||
|
||||
auto_gchar gchar* message = NULL;
|
||||
|
||||
get_message_from_editor_async(rl_line_buffer);
|
||||
if (get_message_from_editor(rl_line_buffer, &message)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rl_replace_line(message, 0);
|
||||
ui_resize();
|
||||
rl_point = rl_end;
|
||||
rl_forced_update_display();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,5 @@ void inp_win_resize(void);
|
||||
void inp_put_back(void);
|
||||
char* inp_get_password(void);
|
||||
char* inp_get_line(void);
|
||||
void inp_set_line(const char* const new_line);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#ifndef UI_TITLEBAR_H
|
||||
#define UI_TITLEBAR_H
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
void create_title_bar(void);
|
||||
void free_title_bar(void);
|
||||
void title_bar_update_virtual(void);
|
||||
|
||||
@@ -351,7 +351,6 @@ void cons_theme_properties(void);
|
||||
void cons_theme_colours(void);
|
||||
void cons_show_tlscert(const TLSCertificate* cert);
|
||||
void cons_show_tlscert_summary(const TLSCertificate* cert);
|
||||
void cons_encryption_setting(void);
|
||||
|
||||
void cons_alert(ProfWin* alert_origin_window);
|
||||
void cons_remove_alert(ProfWin* window);
|
||||
|
||||
@@ -188,7 +188,6 @@ typedef struct prof_chat_win_t
|
||||
char* last_message;
|
||||
char* last_msg_id;
|
||||
gboolean has_attention;
|
||||
guint last_attempted_msg_hash;
|
||||
} ProfChatWin;
|
||||
|
||||
typedef struct prof_muc_win_t
|
||||
|
||||
@@ -164,7 +164,6 @@ win_create_chat(const char* const barejid)
|
||||
new_win->last_message = NULL;
|
||||
new_win->last_msg_id = NULL;
|
||||
new_win->has_attention = FALSE;
|
||||
new_win->last_attempted_msg_hash = 0;
|
||||
new_win->memcheck = PROFCHATWIN_MEMCHECK;
|
||||
|
||||
return &new_win->window;
|
||||
@@ -2003,10 +2002,6 @@ win_redraw(ProfWin* window)
|
||||
_win_print_internal(window, e->show_char, e->pad_indent, e->time, e->flags, e->theme_item, e->display_from, e->message, e->receipt);
|
||||
}
|
||||
e->y_end_pos = getcury(window->layout->win);
|
||||
// Recalculate lines (might be incorrect in case of NCurses overflow)
|
||||
window->layout->buffer->lines -= e->_lines;
|
||||
e->_lines = e->y_end_pos - e->y_start_pos;
|
||||
window->layout->buffer->lines += e->_lines;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#ifndef XMPP_BLOCKING_H
|
||||
#define XMPP_BLOCKING_H
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
void blocking_request(void);
|
||||
int blocked_set_handler(xmpp_stanza_t* stanza);
|
||||
|
||||
|
||||
@@ -312,7 +312,6 @@ iq_reg2_cb(xmpp_conn_t* xmpp_conn, xmpp_stanza_t* stanza, void* userdata)
|
||||
goto quit;
|
||||
|
||||
quit:
|
||||
log_debug("[CONNDBG] iq_reg2_cb: disconnecting after registration completion");
|
||||
xmpp_disconnect(xmpp_conn);
|
||||
|
||||
return 0;
|
||||
@@ -551,7 +550,6 @@ connection_disconnect(void)
|
||||
// don't disconnect already disconnected connection,
|
||||
// or we get infinite loop otherwise
|
||||
if (conn.conn_last_event == XMPP_CONN_CONNECT) {
|
||||
log_debug("[CONNDBG] connection_disconnect: user-initiated disconnect (status=%d)", (int)conn.conn_status);
|
||||
conn.conn_status = JABBER_DISCONNECTING;
|
||||
xmpp_disconnect(conn.xmpp_conn);
|
||||
|
||||
@@ -559,7 +557,6 @@ connection_disconnect(void)
|
||||
session_process_events();
|
||||
}
|
||||
} else {
|
||||
log_debug("[CONNDBG] connection_disconnect: already disconnected (last_event=%d)", (int)conn.conn_last_event);
|
||||
conn.conn_status = JABBER_DISCONNECTED;
|
||||
}
|
||||
|
||||
@@ -982,7 +979,6 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status
|
||||
|
||||
connection_get_jid();
|
||||
conn.domain = strdup(conn.jid->domainpart);
|
||||
log_debug("conn.domain=%s", conn.jid->domainpart);
|
||||
|
||||
connection_clear_data();
|
||||
conn.features_by_jid = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_hash_table_destroy);
|
||||
@@ -1022,9 +1018,6 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status
|
||||
// disconnected
|
||||
case XMPP_CONN_DISCONNECT:
|
||||
log_debug("Connection handler: XMPP_CONN_DISCONNECT");
|
||||
log_debug("[CONNDBG] disconnect: previous_status=%d error=%d has_stream_error=%s",
|
||||
(int)conn.conn_status, error,
|
||||
(stream_error && stream_error->stanza) ? "yes" : "no");
|
||||
|
||||
// lost connection for unknown reason
|
||||
if (conn.conn_status == JABBER_CONNECTED || conn.conn_status == JABBER_DISCONNECTING) {
|
||||
@@ -1170,32 +1163,3 @@ connection_get_profanity_identifier(void)
|
||||
{
|
||||
return prof_identifier;
|
||||
}
|
||||
|
||||
void
|
||||
connection_debug_print_features()
|
||||
{
|
||||
log_debug("=== Connection Features ===");
|
||||
|
||||
GHashTableIter iter;
|
||||
gpointer jid_ptr, features_ptr;
|
||||
|
||||
g_hash_table_iter_init(&iter, conn.features_by_jid);
|
||||
while (g_hash_table_iter_next(&iter, &jid_ptr, &features_ptr)) {
|
||||
const char* jid = (const char*)jid_ptr;
|
||||
GHashTable* features = (GHashTable*)features_ptr;
|
||||
|
||||
if (!features || g_hash_table_size(features) == 0) {
|
||||
log_debug("%s:\t(no features)", jid);
|
||||
continue;
|
||||
}
|
||||
|
||||
GList* feature_keys = g_hash_table_get_keys(features);
|
||||
for (GList* l = feature_keys; l != NULL; l = l->next) {
|
||||
const char* feature = (const char*)l->data;
|
||||
log_debug("%s:\t%s", jid, feature);
|
||||
}
|
||||
g_list_free(feature_keys);
|
||||
}
|
||||
|
||||
log_debug("=== End of Features ===");
|
||||
}
|
||||
|
||||
@@ -1417,8 +1417,6 @@ _manual_pong_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean autoping_error_shown = false;
|
||||
|
||||
static int
|
||||
_autoping_timed_send(xmpp_conn_t* const conn, void* const userdata)
|
||||
{
|
||||
@@ -1427,13 +1425,10 @@ _autoping_timed_send(xmpp_conn_t* const conn, void* const userdata)
|
||||
}
|
||||
|
||||
if (connection_supports(XMPP_FEATURE_PING) == FALSE) {
|
||||
// TODO: do we need to check it on each autoping call?
|
||||
log_warning("Server doesn't advertise %s feature.", XMPP_FEATURE_PING);
|
||||
if (!autoping_error_shown) {
|
||||
connection_debug_print_features();
|
||||
cons_show_error("Server ping not supported (%s). Check log for details.", XMPP_FEATURE_PING);
|
||||
}
|
||||
autoping_error_shown = 1;
|
||||
log_warning("Server doesn't advertise %s feature, disabling autoping.", XMPP_FEATURE_PING);
|
||||
prefs_set_autoping(0);
|
||||
cons_show_error("Server ping not supported (%s), autoping disabled.", XMPP_FEATURE_PING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (autoping_wait) {
|
||||
@@ -1504,7 +1499,7 @@ _auto_pong_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
|
||||
if (g_strcmp0(errtype, STANZA_TYPE_CANCEL) == 0) {
|
||||
log_warning("Server ping (id=%s) error type 'cancel', disabling autoping.", id);
|
||||
prefs_set_autoping(0);
|
||||
cons_show_error("Autoping is not supported by the server (stanza cancelled). The feature has been disabled automatically.");
|
||||
cons_show_error("Server ping not supported, autoping disabled.");
|
||||
xmpp_timed_handler_delete(connection_get_conn(), _autoping_timed_send);
|
||||
}
|
||||
|
||||
@@ -2520,7 +2515,6 @@ _disco_items_result_handler(xmpp_stanza_t* const stanza)
|
||||
GSList* items = NULL;
|
||||
|
||||
if ((g_strcmp0(id, "discoitemsreq") != 0) && (g_strcmp0(id, "discoitemsreq_onconnect") != 0)) {
|
||||
log_warning("_disco_items_result_handler: Received unexpected disco id: %s", id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#ifndef XMPP_IQ_H
|
||||
#define XMPP_IQ_H
|
||||
|
||||
#include <strophe.h>
|
||||
|
||||
typedef int (*ProfIqCallback)(xmpp_stanza_t* const stanza, void* const userdata);
|
||||
typedef void (*ProfIqFreeCallback)(void* userdata);
|
||||
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
/*!
|
||||
* \page OX OX Implementation
|
||||
*
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
#ifndef XMPP_ROSTER_H
|
||||
#define XMPP_ROSTER_H
|
||||
|
||||
#include "glib.h"
|
||||
#include <strophe.h>
|
||||
|
||||
void roster_request(void);
|
||||
void roster_set_handler(xmpp_stanza_t* const stanza);
|
||||
void roster_result_handler(xmpp_stanza_t* const stanza);
|
||||
|
||||
@@ -218,7 +218,6 @@ session_connect_with_details(const char* const jid, const char* const passwd, co
|
||||
void
|
||||
session_autoping_fail(void)
|
||||
{
|
||||
log_debug("[CONNDBG] session_autoping_fail: autoping timeout, triggering lost connection");
|
||||
session_lost_connection();
|
||||
}
|
||||
|
||||
@@ -265,14 +264,11 @@ session_process_events(void)
|
||||
if ((reconnect_sec != 0) && reconnect_timer) {
|
||||
int elapsed_sec = g_timer_elapsed(reconnect_timer, NULL);
|
||||
if (elapsed_sec > reconnect_sec) {
|
||||
log_debug("[CONNDBG] session_process_events: auto-reconnect triggered after %d seconds (threshold=%d)",
|
||||
elapsed_sec, reconnect_sec);
|
||||
session_reconnect_now();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JABBER_RECONNECT:
|
||||
log_debug("[CONNDBG] session_process_events: JABBER_RECONNECT state, calling session_reconnect_now");
|
||||
session_reconnect_now();
|
||||
break;
|
||||
default:
|
||||
@@ -331,12 +327,12 @@ session_login_success(gboolean secured)
|
||||
|
||||
// logged in with account
|
||||
if (saved_account.name) {
|
||||
log_debug("[CONNDBG] Connection handler: logged in with account name: %s", saved_account.name);
|
||||
log_debug("Connection handler: logged in with account name: %s", saved_account.name);
|
||||
sv_ev_login_account_success(saved_account.name, secured);
|
||||
|
||||
// logged in without account, use details to create new account
|
||||
} else {
|
||||
log_debug("[CONNDBG] Connection handler: logged in with jid: %s", saved_details.name);
|
||||
log_debug("Connection handler: logged in with jid: %s", saved_details.name);
|
||||
accounts_add(saved_details.name, saved_details.altdomain, saved_details.port, saved_details.tls_policy, saved_details.auth_policy);
|
||||
accounts_set_jid(saved_details.name, saved_details.jid);
|
||||
|
||||
@@ -375,11 +371,11 @@ void
|
||||
session_login_failed(void)
|
||||
{
|
||||
if (reconnect_timer == NULL) {
|
||||
log_debug("[CONNDBG] Connection handler: No reconnect timer");
|
||||
log_debug("Connection handler: No reconnect timer");
|
||||
sv_ev_failed_login();
|
||||
_session_free_internals();
|
||||
} else {
|
||||
log_debug("[CONNDBG] Connection handler: Restarting reconnect timer");
|
||||
log_debug("Connection handler: Restarting reconnect timer");
|
||||
if (prefs_get_reconnect() != 0) {
|
||||
g_timer_start(reconnect_timer);
|
||||
}
|
||||
@@ -393,16 +389,12 @@ session_login_failed(void)
|
||||
void
|
||||
session_lost_connection(void)
|
||||
{
|
||||
log_debug("[CONNDBG] session_lost_connection: connection lost, reconnect_interval=%d",
|
||||
prefs_get_reconnect());
|
||||
/* this callback also clears all cached data */
|
||||
sv_ev_lost_connection();
|
||||
if (prefs_get_reconnect() != 0) {
|
||||
assert(reconnect_timer == NULL);
|
||||
reconnect_timer = g_timer_new();
|
||||
log_debug("[CONNDBG] session_lost_connection: reconnect timer started");
|
||||
} else {
|
||||
log_debug("[CONNDBG] session_lost_connection: auto-reconnect disabled, cleaning up");
|
||||
_session_free_internals();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "ui/win_types.h"
|
||||
#include "xmpp/vcard.h"
|
||||
#include <strophe.h>
|
||||
|
||||
vCard* vcard_new();
|
||||
void vcard_free(vCard* vcard);
|
||||
|
||||
@@ -208,8 +208,6 @@ const char* connection_jid_for_feature(const char* const feature);
|
||||
|
||||
const char* connection_get_profanity_identifier(void);
|
||||
|
||||
void connection_debug_print_features();
|
||||
|
||||
char* message_send_chat(const char* const barejid, const char* const msg, const char* const oob_url, gboolean request_receipt, const char* const replace_id);
|
||||
char* message_send_chat_otr(const char* const barejid, const char* const msg, gboolean request_receipt, const char* const replace_id);
|
||||
char* message_send_chat_pgp(const char* const barejid, const char* const msg, gboolean request_receipt, const char* const replace_id);
|
||||
|
||||
@@ -1,30 +1,10 @@
|
||||
/*
|
||||
* functionaltests.c
|
||||
*
|
||||
* Functional tests for CProof XMPP client.
|
||||
* Uses cmocka framework with stabber mock XMPP server.
|
||||
*
|
||||
* Each test is wrapped with PROF_FUNC_TEST macro which sets up
|
||||
* init_prof_test (starts stabber server and profanity client)
|
||||
* and close_prof_test (cleanup) as setup/teardown functions.
|
||||
*
|
||||
* NOTE: We restart client and server for each test to ensure complete
|
||||
* isolation. This prevents state leakage between tests (roster entries,
|
||||
* MUC rooms, presence subscriptions, etc.). While slower, it eliminates
|
||||
* flaky tests caused by leftover state. The overhead is acceptable since
|
||||
* functional tests run less frequently than unit tests.
|
||||
*
|
||||
* Tests are organized into groups for better maintainability:
|
||||
* Group 1: Connection, Ping, Rooms, Presence
|
||||
* Group 2: Messages, Receipts, Roster management
|
||||
* Group 3: MUC (Multi-User Chat) functionality
|
||||
* Group 4: Carbons, Chat sessions, Software version, Disconnect
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "config.h"
|
||||
@@ -43,41 +23,30 @@
|
||||
#include "test_muc.h"
|
||||
#include "test_disconnect.h"
|
||||
|
||||
/* Macro to wrap each test with setup/teardown functions */
|
||||
#define PROF_FUNC_TEST(test) cmocka_unit_test_setup_teardown(test, init_prof_test, close_prof_test)
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
|
||||
const struct CMUnitTest all_tests[] = {
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 1: Connect, Ping, Rooms, Presence
|
||||
* Basic XMPP session establishment and presence management
|
||||
* ============================================================ */
|
||||
|
||||
/* Connection tests - verify login, roster, bookmarks */
|
||||
PROF_FUNC_TEST(connect_jid_requests_roster),
|
||||
PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster),
|
||||
PROF_FUNC_TEST(connect_jid_requests_bookmarks),
|
||||
PROF_FUNC_TEST(connect_bad_password),
|
||||
PROF_FUNC_TEST(connect_shows_presence_updates),
|
||||
|
||||
/* Ping tests - XEP-0199 XMPP Ping */
|
||||
PROF_FUNC_TEST(ping_server),
|
||||
PROF_FUNC_TEST(ping_server_not_supported),
|
||||
PROF_FUNC_TEST(ping_responds_to_server_request),
|
||||
PROF_FUNC_TEST(ping_jid),
|
||||
PROF_FUNC_TEST(ping_jid_not_supported),
|
||||
|
||||
/* Room discovery - XEP-0045 */
|
||||
PROF_FUNC_TEST(rooms_query),
|
||||
|
||||
/* Presence tests - online/away/xa/dnd/chat status */
|
||||
PROF_FUNC_TEST(presence_online),
|
||||
PROF_FUNC_TEST(presence_online_with_message),
|
||||
PROF_FUNC_TEST(presence_away),
|
||||
PROF_FUNC_TEST(presence_away_with_message),
|
||||
PROF_FUNC_TEST(presence_online),
|
||||
PROF_FUNC_TEST(presence_online_with_message),
|
||||
PROF_FUNC_TEST(presence_xa),
|
||||
PROF_FUNC_TEST(presence_xa_with_message),
|
||||
PROF_FUNC_TEST(presence_dnd),
|
||||
@@ -90,69 +59,10 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(presence_received),
|
||||
PROF_FUNC_TEST(presence_missing_resource_defaults),
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 2: Message, Receipts, Roster
|
||||
* Core messaging and contact management
|
||||
* ============================================================ */
|
||||
|
||||
/* Basic message send/receive */
|
||||
PROF_FUNC_TEST(message_send),
|
||||
PROF_FUNC_TEST(message_receive_console),
|
||||
PROF_FUNC_TEST(message_receive_chatwin),
|
||||
|
||||
/* Message receipts - XEP-0184 */
|
||||
PROF_FUNC_TEST(does_not_send_receipt_request_to_barejid),
|
||||
PROF_FUNC_TEST(send_receipt_request),
|
||||
PROF_FUNC_TEST(send_receipt_on_request),
|
||||
|
||||
/* Roster management - add/remove/rename contacts */
|
||||
PROF_FUNC_TEST(sends_new_item),
|
||||
PROF_FUNC_TEST(sends_new_item_nick),
|
||||
PROF_FUNC_TEST(sends_remove_item),
|
||||
PROF_FUNC_TEST(sends_remove_item_nick),
|
||||
PROF_FUNC_TEST(sends_nick_change),
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 3: MUC (Multi-User Chat)
|
||||
* XEP-0045 conference room functionality
|
||||
* ============================================================ */
|
||||
|
||||
/* Room join with various options */
|
||||
PROF_FUNC_TEST(sends_room_join),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick),
|
||||
PROF_FUNC_TEST(sends_room_join_with_password),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
|
||||
|
||||
/* Room information display */
|
||||
PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
|
||||
PROF_FUNC_TEST(shows_subject_on_join),
|
||||
// PROF_FUNC_TEST(shows_history_message), // temporarily disabled due to timing issues in CI
|
||||
PROF_FUNC_TEST(shows_occupant_join),
|
||||
|
||||
/* MUC messaging */
|
||||
PROF_FUNC_TEST(shows_message),
|
||||
PROF_FUNC_TEST(shows_me_message_from_occupant),
|
||||
PROF_FUNC_TEST(shows_me_message_from_self),
|
||||
|
||||
/* Console notification settings for MUC */
|
||||
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
|
||||
|
||||
/* ============================================================
|
||||
* GROUP 4: Carbons, Chat Session, Software, Disconnect
|
||||
* Message synchronization and session management
|
||||
* ============================================================ */
|
||||
|
||||
/* Message Carbons - XEP-0280 (message sync across devices) */
|
||||
PROF_FUNC_TEST(send_enable_carbons),
|
||||
PROF_FUNC_TEST(connect_with_carbons_enabled),
|
||||
PROF_FUNC_TEST(send_disable_carbons),
|
||||
PROF_FUNC_TEST(receive_carbon),
|
||||
PROF_FUNC_TEST(receive_self_carbon),
|
||||
PROF_FUNC_TEST(receive_private_carbon),
|
||||
|
||||
/* Chat session management - bare/full JID routing */
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
|
||||
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
|
||||
PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
|
||||
@@ -160,7 +70,22 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
|
||||
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
|
||||
|
||||
/* Software Version - XEP-0092 */
|
||||
PROF_FUNC_TEST(send_enable_carbons),
|
||||
PROF_FUNC_TEST(connect_with_carbons_enabled),
|
||||
PROF_FUNC_TEST(send_disable_carbons),
|
||||
PROF_FUNC_TEST(receive_carbon),
|
||||
PROF_FUNC_TEST(receive_self_carbon),
|
||||
PROF_FUNC_TEST(receive_private_carbon),
|
||||
|
||||
PROF_FUNC_TEST(send_receipt_request),
|
||||
PROF_FUNC_TEST(send_receipt_on_request),
|
||||
PROF_FUNC_TEST(does_not_send_receipt_request_to_barejid),
|
||||
PROF_FUNC_TEST(sends_new_item),
|
||||
PROF_FUNC_TEST(sends_new_item_nick),
|
||||
PROF_FUNC_TEST(sends_remove_item),
|
||||
PROF_FUNC_TEST(sends_remove_item_nick),
|
||||
PROF_FUNC_TEST(sends_nick_change),
|
||||
|
||||
PROF_FUNC_TEST(send_software_version_request),
|
||||
PROF_FUNC_TEST(display_software_version_result),
|
||||
PROF_FUNC_TEST(shows_message_when_software_version_error),
|
||||
@@ -168,7 +93,21 @@ main(int argc, char* argv[])
|
||||
PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
|
||||
PROF_FUNC_TEST(display_software_version_result_in_chat),
|
||||
|
||||
/* Disconnect - clean session termination */
|
||||
PROF_FUNC_TEST(sends_room_join),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick),
|
||||
PROF_FUNC_TEST(sends_room_join_with_password),
|
||||
PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
|
||||
PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
|
||||
PROF_FUNC_TEST(shows_subject_on_join),
|
||||
PROF_FUNC_TEST(shows_history_message),
|
||||
PROF_FUNC_TEST(shows_occupant_join),
|
||||
PROF_FUNC_TEST(shows_message),
|
||||
PROF_FUNC_TEST(shows_me_message_from_occupant),
|
||||
PROF_FUNC_TEST(shows_me_message_from_self),
|
||||
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
|
||||
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
|
||||
|
||||
PROF_FUNC_TEST(disconnect_ends_session),
|
||||
};
|
||||
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
#include <sys/wait.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <cmocka.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <pty.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <regex.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -21,20 +21,6 @@ char *config_orig;
|
||||
char *data_orig;
|
||||
|
||||
int fd = 0;
|
||||
int stub_port = 5230;
|
||||
pid_t child_pid = 0;
|
||||
|
||||
/*
|
||||
* Buffer for accumulating output from profanity.
|
||||
* 64KB is sufficient for typical test output while keeping memory usage
|
||||
* reasonable. When full, older half is discarded (ring buffer behavior).
|
||||
*/
|
||||
#define OUTPUT_BUF_SIZE 65536
|
||||
static char output_buffer[OUTPUT_BUF_SIZE];
|
||||
static size_t output_len = 0;
|
||||
|
||||
/* Timeout for expect operations in seconds */
|
||||
static int expect_timeout = 30;
|
||||
|
||||
gboolean
|
||||
_create_dir(const char *name)
|
||||
@@ -135,105 +121,31 @@ _cleanup_dirs(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read available data from fd into output_buffer with timeout.
|
||||
* Returns number of bytes read, 0 on timeout, -1 on error.
|
||||
*/
|
||||
static int
|
||||
_read_output(int timeout_ms)
|
||||
{
|
||||
fd_set readfds;
|
||||
struct timeval tv;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(fd, &readfds);
|
||||
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000) * 1000;
|
||||
|
||||
int ret = select(fd + 1, &readfds, NULL, NULL, &tv);
|
||||
if (ret <= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t space = OUTPUT_BUF_SIZE - output_len - 1;
|
||||
if (space <= 0) {
|
||||
/* Buffer full, shift content */
|
||||
memmove(output_buffer, output_buffer + OUTPUT_BUF_SIZE/2, OUTPUT_BUF_SIZE/2);
|
||||
output_len = OUTPUT_BUF_SIZE/2;
|
||||
space = OUTPUT_BUF_SIZE - output_len - 1;
|
||||
}
|
||||
|
||||
ssize_t n = read(fd, output_buffer + output_len, space);
|
||||
if (n > 0) {
|
||||
output_len += n;
|
||||
output_buffer[output_len] = '\0';
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom implementation of exp_spawnl using forkpty.
|
||||
* This avoids the segfault bug in libexpect on Arch Linux.
|
||||
*/
|
||||
void
|
||||
prof_start(void)
|
||||
{
|
||||
struct winsize ws;
|
||||
ws.ws_row = 24;
|
||||
ws.ws_col = 300; /* Match COLUMNS=300 from start_profanity.sh */
|
||||
ws.ws_xpixel = 0;
|
||||
ws.ws_ypixel = 0;
|
||||
|
||||
/* Reset output buffer */
|
||||
output_len = 0;
|
||||
output_buffer[0] = '\0';
|
||||
|
||||
child_pid = forkpty(&fd, NULL, NULL, &ws);
|
||||
|
||||
if (child_pid < 0) {
|
||||
fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
/* Child process */
|
||||
setenv("COLUMNS", "300", 1);
|
||||
setenv("TERM", "xterm", 1);
|
||||
|
||||
execl("./profanity", "./profanity", "-l", "DEBUG", NULL);
|
||||
|
||||
/* If exec fails */
|
||||
fprintf(stderr, "execl failed: %s\n", strerror(errno));
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* Parent process */
|
||||
/* Set non-blocking mode for reading */
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
// helper script sets terminal columns, avoids assertions failing
|
||||
// based on the test runner terminal size
|
||||
fd = exp_spawnl("sh",
|
||||
"sh",
|
||||
"-c",
|
||||
"./tests/functionaltests/start_profanity.sh",
|
||||
NULL);
|
||||
FILE *fp = fdopen(fd, "r+");
|
||||
|
||||
assert_true(fp != NULL);
|
||||
|
||||
setbuf(fp, (char *)0);
|
||||
}
|
||||
|
||||
int
|
||||
init_prof_test(void **state)
|
||||
{
|
||||
gboolean started = FALSE;
|
||||
for (int p = 5230; p < 5250; ++p) {
|
||||
int ret = stbbr_start(STBBR_LOGDEBUG, p, 0);
|
||||
if (ret == 0) {
|
||||
stub_port = p;
|
||||
started = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!started) {
|
||||
assert_true(FALSE); // could not start stabber on any port in range
|
||||
if (stbbr_start(STBBR_LOGDEBUG ,5230, 0) != 0) {
|
||||
assert_true(FALSE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Give stabber server thread time to start listening
|
||||
usleep(100000); // 100ms
|
||||
|
||||
config_orig = getenv("XDG_CONFIG_HOME");
|
||||
data_orig = getenv("XDG_DATA_HOME");
|
||||
|
||||
@@ -248,69 +160,50 @@ init_prof_test(void **state)
|
||||
_create_logs_dir();
|
||||
|
||||
prof_start();
|
||||
int prof_started = prof_output_regex("CProof\\. Type /help for help information\\.");
|
||||
assert_true(prof_started);
|
||||
assert_true(prof_output_exact("Profanity"));
|
||||
|
||||
// set UI options to make expect assertions faster and more reliable
|
||||
prof_input("/inpblock timeout 5");
|
||||
assert_true(prof_output_regex("Input blocking set to 5 milliseconds"));
|
||||
assert_true(prof_output_exact("Input blocking set to 5 milliseconds"));
|
||||
prof_input("/inpblock dynamic off");
|
||||
assert_true(prof_output_regex("Dynamic input blocking disabled"));
|
||||
assert_true(prof_output_exact("Dynamic input blocking disabled"));
|
||||
prof_input("/notify chat off");
|
||||
assert_true(prof_output_regex("Chat notifications disabled"));
|
||||
assert_true(prof_output_exact("Chat notifications disabled"));
|
||||
prof_input("/notify room off");
|
||||
assert_true(prof_output_regex("Room notifications disabled"));
|
||||
assert_true(prof_output_exact("Room notifications disabled"));
|
||||
prof_input("/wrap off");
|
||||
assert_true(prof_output_regex("Word wrap disabled"));
|
||||
assert_true(prof_output_exact("Word wrap disabled"));
|
||||
prof_input("/roster hide");
|
||||
assert_true(prof_output_regex("Roster disabled"));
|
||||
assert_true(prof_output_exact("Roster disabled"));
|
||||
prof_input("/occupants default hide");
|
||||
assert_true(prof_output_regex("Occupant list disabled"));
|
||||
assert_true(prof_output_exact("Occupant list disabled"));
|
||||
prof_input("/time console off");
|
||||
prof_input("/time console off");
|
||||
assert_true(prof_output_regex("Console time display disabled\\."));
|
||||
assert_true(prof_output_exact("Console time display disabled."));
|
||||
prof_input("/time chat off");
|
||||
assert_true(prof_output_regex("Chat time display disabled\\."));
|
||||
assert_true(prof_output_exact("Chat time display disabled."));
|
||||
prof_input("/time muc off");
|
||||
assert_true(prof_output_regex("MUC time display disabled\\."));
|
||||
assert_true(prof_output_exact("MUC time display disabled."));
|
||||
prof_input("/time config off");
|
||||
assert_true(prof_output_regex("Config time display disabled\\."));
|
||||
assert_true(prof_output_exact("config time display disabled."));
|
||||
prof_input("/time private off");
|
||||
assert_true(prof_output_regex("Private chat time display disabled\\."));
|
||||
assert_true(prof_output_exact("Private chat time display disabled."));
|
||||
prof_input("/time xml off");
|
||||
assert_true(prof_output_regex("XML Console time display disabled\\."));
|
||||
assert_true(prof_output_exact("XML Console time display disabled."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
close_prof_test(void **state)
|
||||
{
|
||||
if (fd > 0 && child_pid > 0) {
|
||||
prof_input("/quit");
|
||||
// Give profanity time to process quit command
|
||||
sleep(1);
|
||||
waitpid(child_pid, NULL, 0);
|
||||
close(fd);
|
||||
fd = 0;
|
||||
child_pid = 0;
|
||||
}
|
||||
prof_input("/quit");
|
||||
waitpid(exp_pid, NULL, 0);
|
||||
_cleanup_dirs();
|
||||
|
||||
if (config_orig) {
|
||||
setenv("XDG_CONFIG_HOME", config_orig, 1);
|
||||
}
|
||||
if (data_orig) {
|
||||
setenv("XDG_DATA_HOME", data_orig, 1);
|
||||
}
|
||||
setenv("XDG_CONFIG_HOME", config_orig, 1);
|
||||
setenv("XDG_DATA_HOME", data_orig, 1);
|
||||
|
||||
stbbr_stop();
|
||||
/*
|
||||
* TODO: Replace with proper synchronization.
|
||||
* stabber doesn't provide wait_stopped() API yet, so we use delay
|
||||
* to ensure the port is released before the next test starts.
|
||||
* See: https://git.jabber.space/devs/stabber/issues/3
|
||||
*/
|
||||
usleep(100000); // 100ms
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -319,75 +212,20 @@ prof_input(const char *input)
|
||||
{
|
||||
GString *inp_str = g_string_new(input);
|
||||
g_string_append(inp_str, "\r");
|
||||
ssize_t _wn = write(fd, inp_str->str, inp_str->len);
|
||||
(void)_wn;
|
||||
write(fd, inp_str->str, inp_str->len);
|
||||
g_string_free(inp_str, TRUE);
|
||||
|
||||
/* Small delay to let profanity process input */
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for exact text to appear in output.
|
||||
* Returns 1 if found, 0 if timeout.
|
||||
*/
|
||||
int
|
||||
prof_output_exact(const char *text)
|
||||
{
|
||||
time_t start = time(NULL);
|
||||
|
||||
while (time(NULL) - start < expect_timeout) {
|
||||
/* Read any available output */
|
||||
while (_read_output(100) > 0) {
|
||||
/* Keep reading while data available */
|
||||
}
|
||||
|
||||
/* Check if text is in buffer */
|
||||
if (strstr(output_buffer, text) != NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
usleep(50000); /* 50ms */
|
||||
}
|
||||
|
||||
return 0;
|
||||
return (1 == exp_expectl(fd, exp_exact, text, 1, exp_end));
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for regex pattern to match in output.
|
||||
* Returns 1 if found, 0 if timeout.
|
||||
*/
|
||||
int
|
||||
prof_output_regex(const char *pattern)
|
||||
prof_output_regex(const char *text)
|
||||
{
|
||||
regex_t regex;
|
||||
int ret;
|
||||
|
||||
ret = regcomp(®ex, pattern, REG_EXTENDED | REG_NOSUB);
|
||||
if (ret != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t start = time(NULL);
|
||||
|
||||
while (time(NULL) - start < expect_timeout) {
|
||||
/* Read any available output */
|
||||
while (_read_output(100) > 0) {
|
||||
/* Keep reading while data available */
|
||||
}
|
||||
|
||||
/* Check if pattern matches */
|
||||
ret = regexec(®ex, output_buffer, 0, NULL, 0);
|
||||
if (ret == 0) {
|
||||
regfree(®ex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
usleep(50000); /* 50ms */
|
||||
}
|
||||
|
||||
regfree(®ex);
|
||||
return 0;
|
||||
return (1 == exp_expectl(fd, exp_regexp, text, 1, exp_end));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -406,40 +244,33 @@ prof_connect_with_roster(const char *roster)
|
||||
stbbr_for_query("jabber:iq:roster", roster_str->str);
|
||||
g_string_free(roster_str, TRUE);
|
||||
|
||||
stbbr_auth_passwd("password");
|
||||
stbbr_for_id("prof_presence_1",
|
||||
"<presence id='prof_presence_1' lang='en' to='stabber@localhost/profanity' from='stabber@localhost/profanity'>"
|
||||
"<priority>0</priority>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io/' ver='f8mrtdyAmhnj8Ca+630bThSL718='/>"
|
||||
"</presence>"
|
||||
);
|
||||
|
||||
char connect_cmd[128];
|
||||
snprintf(connect_cmd, sizeof(connect_cmd), "/connect stabber@localhost server 127.0.0.1 port %d tls disable auth legacy", stub_port);
|
||||
prof_input(connect_cmd);
|
||||
|
||||
assert_true(prof_output_regex("password:"));
|
||||
prof_input("/connect stabber@localhost server 127.0.0.1 port 5230 tls allow");
|
||||
prof_input("password");
|
||||
|
||||
expect_timeout = 60;
|
||||
assert_true(prof_output_regex("Connecting as stabber@localhost"));
|
||||
assert_true(prof_output_regex("logged in successfully"));
|
||||
assert_true(prof_output_regex(".+online.+ \\(priority 0\\)\\."));
|
||||
|
||||
expect_timeout = 60;
|
||||
// Wait for presence stanza to be sent (content-based, not ID-based)
|
||||
// Match the actual attribute order from stanza_attach_caps
|
||||
assert_true(stbbr_received(
|
||||
"<presence id=\"*\">"
|
||||
"<c xmlns=\"http://jabber.org/protocol/caps\" hash=\"sha-1\" node=\"http://profanity-im.github.io\" ver=\"*\"/>"
|
||||
"</presence>"
|
||||
));
|
||||
// Allow time for profanity to connect
|
||||
exp_timeout = 30;
|
||||
assert_true(prof_output_regex("stabber@localhost/profanity logged in successfully, .+online.+ \\(priority 0\\)\\."));
|
||||
exp_timeout = 10;
|
||||
stbbr_wait_for("prof_presence_*");
|
||||
}
|
||||
|
||||
void
|
||||
prof_timeout(int timeout)
|
||||
{
|
||||
expect_timeout = timeout;
|
||||
exp_timeout = timeout;
|
||||
}
|
||||
|
||||
void
|
||||
prof_timeout_reset(void)
|
||||
{
|
||||
expect_timeout = 60;
|
||||
exp_timeout = 10;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
#define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home"
|
||||
#define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home"
|
||||
|
||||
extern int stub_port;
|
||||
|
||||
int init_prof_test(void **state);
|
||||
int close_prof_test(void **state);
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -144,11 +147,7 @@ resets_to_barejid_after_presence_received(void **state)
|
||||
"<show>dnd</show>"
|
||||
"</presence>"
|
||||
);
|
||||
// Wait for presence to be processed before sending next message.
|
||||
// The presence output may appear in console or chat window depending on focus,
|
||||
// so we use a small delay to ensure the session is reset to barejid.
|
||||
// Under Valgrind, processing is slower, so this delay is necessary.
|
||||
g_usleep(500000); // 500ms
|
||||
assert_true(prof_output_exact("Buddy1 (laptop) is dnd"));
|
||||
|
||||
prof_input("/msg buddy1@localhost Outgoing 2");
|
||||
assert_true(stbbr_received(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -47,9 +50,7 @@ connect_jid_requests_bookmarks(void **state)
|
||||
void
|
||||
connect_bad_password(void **state)
|
||||
{
|
||||
char connect_cmd[128];
|
||||
snprintf(connect_cmd, sizeof(connect_cmd), "/connect stabber@localhost server 127.0.0.1 port %d tls allow", stub_port);
|
||||
prof_input(connect_cmd);
|
||||
prof_input("/connect stabber@localhost server 127.0.0.1 port 5230 tls allow");
|
||||
prof_input("badpassword");
|
||||
|
||||
assert_true(prof_output_exact("Login failed."));
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -23,9 +27,6 @@ message_send(void **state)
|
||||
assert_true(prof_output_regex("me: .+Hi there"));
|
||||
}
|
||||
|
||||
// TODO: `/message correct` XEP-0308 compliance (whether each correction links to the original message ID)
|
||||
// https://xmpp.org/extensions/xep-0308.html#rules
|
||||
|
||||
void
|
||||
message_receive_console(void **state)
|
||||
{
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
/*
|
||||
* NOTE: We use prof_output_regex() throughout this file even for seemingly
|
||||
* exact strings because some output may include timestamps, color codes,
|
||||
* or other dynamic content depending on configuration. Using regex provides
|
||||
* more robust matching. For patterns without regex metacharacters, the
|
||||
* performance difference is negligible.
|
||||
*/
|
||||
|
||||
void
|
||||
sends_room_join(void **state)
|
||||
{
|
||||
@@ -23,7 +18,7 @@ sends_room_join(void **state)
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
assert_true(stbbr_last_received(
|
||||
"<presence id='*' to='testroom@conference.localhost/stabber'>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc'/>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
@@ -38,7 +33,7 @@ sends_room_join_with_nick(void **state)
|
||||
|
||||
prof_input("/join testroom@conference.localhost nick testnick");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
assert_true(stbbr_last_received(
|
||||
"<presence id='*' to='testroom@conference.localhost/testnick'>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc'/>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
@@ -53,7 +48,7 @@ sends_room_join_with_password(void **state)
|
||||
|
||||
prof_input("/join testroom@conference.localhost password testpassword");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
assert_true(stbbr_last_received(
|
||||
"<presence id='*' to='testroom@conference.localhost/stabber'>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc'>"
|
||||
"<password>testpassword</password>"
|
||||
@@ -70,7 +65,7 @@ sends_room_join_with_nick_and_password(void **state)
|
||||
|
||||
prof_input("/join testroom@conference.localhost nick testnick password testpassword");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
assert_true(stbbr_last_received(
|
||||
"<presence id='*' to='testroom@conference.localhost/testnick'>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc'>"
|
||||
"<password>testpassword</password>"
|
||||
@@ -85,8 +80,8 @@ shows_role_and_affiliation_on_join(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -97,7 +92,7 @@ shows_role_and_affiliation_on_join(void **state)
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -105,8 +100,8 @@ shows_subject_on_join(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -116,7 +111,7 @@ shows_subject_on_join(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost'>"
|
||||
@@ -133,8 +128,8 @@ shows_history_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -144,7 +139,7 @@ shows_history_message(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -154,9 +149,7 @@ shows_history_message(void **state)
|
||||
"</message>"
|
||||
);
|
||||
|
||||
prof_timeout(10);
|
||||
assert_true(prof_output_regex("testoccupant:.*an old message"));
|
||||
prof_timeout_reset();
|
||||
assert_true(prof_output_regex("testoccupant: an old message"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -164,12 +157,8 @@ shows_occupant_join(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
// Enable MUC status messages to see occupant join/leave
|
||||
prof_input("/presence room all");
|
||||
assert_true(prof_output_regex("All presence updates will appear"));
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -179,7 +168,7 @@ shows_occupant_join(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<presence to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -188,9 +177,8 @@ shows_occupant_join(void **state)
|
||||
"</x>"
|
||||
"</presence>"
|
||||
);
|
||||
sleep(1);
|
||||
|
||||
assert_true(prof_output_regex("testoccupant has joined"));
|
||||
assert_true(prof_output_exact("-> testoccupant has joined the room, role: participant, affiliation: none"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -198,8 +186,8 @@ shows_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -209,7 +197,7 @@ shows_message(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -225,8 +213,8 @@ shows_me_message_from_occupant(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -236,7 +224,7 @@ shows_me_message_from_occupant(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -244,7 +232,7 @@ shows_me_message_from_occupant(void **state)
|
||||
"</message>"
|
||||
);
|
||||
|
||||
assert_true(prof_output_regex("\\*testoccupant did something"));
|
||||
assert_true(prof_output_exact("*testoccupant did something"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -252,8 +240,8 @@ shows_me_message_from_self(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -263,7 +251,7 @@ shows_me_message_from_self(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
@@ -271,7 +259,7 @@ shows_me_message_from_self(void **state)
|
||||
"</message>"
|
||||
);
|
||||
|
||||
assert_true(prof_output_regex("\\*stabber did something"));
|
||||
assert_true(prof_output_exact("*stabber did something"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -279,8 +267,8 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -290,10 +278,10 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
prof_input("/win 1");
|
||||
assert_true(prof_output_regex("CProof\\. Type /help for help information\\."));
|
||||
assert_true(prof_output_exact("CProof. Type /help for help information."));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -301,7 +289,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
|
||||
"</message>"
|
||||
);
|
||||
|
||||
assert_true(prof_output_regex("<< room message: testoccupant in testroom@conference\\.localhost \\(win 2\\)"));
|
||||
assert_true(prof_output_exact("<< room message: testoccupant in testroom@conference.localhost (win 2)"));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>"
|
||||
@@ -309,7 +297,7 @@ shows_all_messages_in_console_when_window_not_focussed(void **state)
|
||||
"</message>"
|
||||
);
|
||||
|
||||
assert_true(prof_output_regex("<< room message: anotheroccupant in testroom@conference\\.localhost \\(win 2\\)"));
|
||||
assert_true(prof_output_exact("<< room message: anotheroccupant in testroom@conference.localhost (win 2)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -318,10 +306,10 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
|
||||
prof_connect();
|
||||
|
||||
prof_input("/console muc first");
|
||||
assert_true(prof_output_regex("Console MUC messages set: first"));
|
||||
assert_true(prof_output_exact("Console MUC messages set: first"));
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -331,22 +319,21 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
prof_input("/win 1");
|
||||
assert_true(prof_output_regex("CProof\\. Type /help for help information\\."));
|
||||
assert_true(prof_output_exact("CProof. Type /help for help information."));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
"<body>a new message</body>"
|
||||
"</message>"
|
||||
);
|
||||
sleep(1);
|
||||
|
||||
assert_true(prof_output_regex("room message.*testroom@conference\\.localhost"));
|
||||
assert_true(prof_output_exact("<< room message: testroom@conference.localhost (win 2)"));
|
||||
prof_input("/clear");
|
||||
prof_input("/about");
|
||||
assert_true(prof_output_regex("Type '/help' to show complete help\\."));
|
||||
assert_true(prof_output_exact("Type '/help' to show complete help."));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/anotheroccupant'>"
|
||||
@@ -355,7 +342,7 @@ shows_first_message_in_console_when_window_not_focussed(void **state)
|
||||
);
|
||||
|
||||
prof_timeout(2);
|
||||
assert_false(prof_output_regex("<< room message: testroom@conference\\.localhost \\(win 2\\)"));
|
||||
assert_false(prof_output_exact("<< room message: testroom@conference.localhost (win 2)"));
|
||||
prof_timeout_reset();
|
||||
}
|
||||
|
||||
@@ -365,10 +352,10 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
|
||||
prof_connect();
|
||||
|
||||
prof_input("/console muc none");
|
||||
assert_true(prof_output_regex("Console MUC messages set: none"));
|
||||
assert_true(prof_output_exact("Console MUC messages set: none"));
|
||||
|
||||
stbbr_for_presence_to("testroom@conference.localhost/stabber",
|
||||
"<presence id='*' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
stbbr_for_id("prof_join_4",
|
||||
"<presence id='prof_join_4' lang='en' to='stabber@localhost/profanity' from='testroom@conference.localhost/stabber'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' node='http://profanity-im.github.io' ver='*'/>"
|
||||
"<x xmlns='http://jabber.org/protocol/muc#user'>"
|
||||
"<item role='participant' jid='stabber@localhost/profanity' affiliation='none'/>"
|
||||
@@ -378,10 +365,10 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
|
||||
);
|
||||
|
||||
prof_input("/join testroom@conference.localhost");
|
||||
assert_true(prof_output_regex("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
assert_true(prof_output_exact("-> You have joined the room as stabber, role: participant, affiliation: none"));
|
||||
|
||||
prof_input("/win 1");
|
||||
assert_true(prof_output_regex("CProof\\. Type /help for help information\\."));
|
||||
assert_true(prof_output_exact("CProof. Type /help for help information."));
|
||||
|
||||
stbbr_send(
|
||||
"<message type='groupchat' to='stabber@localhost/profanity' from='testroom@conference.localhost/testoccupant'>"
|
||||
@@ -390,6 +377,6 @@ shows_no_message_in_console_when_window_not_focussed(void **state)
|
||||
);
|
||||
|
||||
prof_timeout(2);
|
||||
assert_false(prof_output_regex("testroom@conference\\.localhost \\(win 2\\)"));
|
||||
assert_false(prof_output_exact("testroom@conference.localhost (win 2)"));
|
||||
prof_timeout_reset();
|
||||
}
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
void
|
||||
ping_server(void **state)
|
||||
{
|
||||
/*
|
||||
* This test verifies that profanity correctly handles the /ping command
|
||||
* when the server supports ping (urn:xmpp:ping feature).
|
||||
*
|
||||
* We register disco#info response with ping support, then verify
|
||||
* profanity sends ping and receives "Pinged server" confirmation.
|
||||
*/
|
||||
|
||||
// Register disco#info response that includes ping support
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#info",
|
||||
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>"
|
||||
stbbr_for_id("prof_disco_info_onconnect_2",
|
||||
"<iq id='prof_disco_info_onconnect_2' to='stabber@localhost/profanity' type='result' from='localhost'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'>"
|
||||
"<identity category='server' type='im' name='Prosody'/>"
|
||||
"<feature var='urn:xmpp:ping'/>"
|
||||
@@ -29,33 +23,37 @@ ping_server(void **state)
|
||||
"</iq>"
|
||||
);
|
||||
|
||||
// Register ping response
|
||||
stbbr_for_query("urn:xmpp:ping",
|
||||
"<iq from='localhost' to='stabber@localhost/profanity' type='result'/>"
|
||||
stbbr_for_id("prof_ping_4",
|
||||
"<iq id='prof_ping_4' type='result' to='stabber@localhost/profanity'/>"
|
||||
);
|
||||
stbbr_for_id("prof_ping_5",
|
||||
"<iq id='prof_ping_5' type='result' to='stabber@localhost/profanity'/>"
|
||||
);
|
||||
|
||||
prof_connect();
|
||||
|
||||
/*
|
||||
* Wait for disco#info exchange to complete.
|
||||
* TODO: Replace with proper wait mechanism (e.g., wait for capabilities
|
||||
* to be cached). Currently we use sleep to ensure disco#info response
|
||||
* is processed before sending /ping command.
|
||||
*/
|
||||
sleep(3);
|
||||
|
||||
prof_input("/ping");
|
||||
|
||||
prof_timeout(10);
|
||||
assert_true(prof_output_regex("Pinged server"));
|
||||
prof_timeout_reset();
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='prof_ping_4' type='get'>"
|
||||
"<ping xmlns='urn:xmpp:ping'/>"
|
||||
"</iq>"
|
||||
));
|
||||
assert_true(prof_output_exact("Ping response from server"));
|
||||
|
||||
prof_input("/ping");
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='prof_ping_5' type='get'>"
|
||||
"<ping xmlns='urn:xmpp:ping'/>"
|
||||
"</iq>"
|
||||
));
|
||||
assert_true(prof_output_exact("Ping response from server"));
|
||||
}
|
||||
|
||||
void
|
||||
ping_server_not_supported(void **state)
|
||||
{
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#info",
|
||||
"<iq to='stabber@localhost/profanity' type='result' from='localhost'>"
|
||||
stbbr_for_id("prof_disco_info_onconnect_2",
|
||||
"<iq id='prof_disco_info_onconnect_2' to='stabber@localhost/profanity' type='result' from='localhost'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info'>"
|
||||
"<identity category='server' type='im' name='Stabber'/>"
|
||||
"</query>"
|
||||
@@ -65,7 +63,7 @@ ping_server_not_supported(void **state)
|
||||
prof_connect();
|
||||
|
||||
prof_input("/ping");
|
||||
assert_true(prof_output_regex("Server does not support ping requests"));
|
||||
assert_true(prof_output_exact("Server does not support ping requests."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -74,21 +72,20 @@ ping_responds_to_server_request(void **state)
|
||||
prof_connect();
|
||||
|
||||
stbbr_send(
|
||||
"<iq id=\"pingtest1\" type=\"get\" to=\"stabber@localhost/profanity\" from=\"localhost\">"
|
||||
"<ping xmlns=\"urn:xmpp:ping\"/>"
|
||||
"<iq id='pingtest1' type='get' to='stabber@localhost/profanity' from='localhost'>"
|
||||
"<ping xmlns='urn:xmpp:ping'/>"
|
||||
"</iq>"
|
||||
);
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='pingtest1' type='result' from='stabber@localhost/profanity' to='localhost'></iq>"
|
||||
"<iq id='pingtest1' type='result' from='stabber@localhost/profanity' to='localhost'/>"
|
||||
));
|
||||
}
|
||||
|
||||
void ping_jid(void **state)
|
||||
{
|
||||
// Caps/disco info response without relying on a fixed id
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#info",
|
||||
"<iq to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
|
||||
stbbr_for_id("prof_caps_4",
|
||||
"<iq id='prof_caps_4' to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>"
|
||||
"<identity category='client' type='console' name='Profanity0.6.0'/>"
|
||||
"<feature var='urn:xmpp:ping'/>"
|
||||
@@ -115,30 +112,29 @@ void ping_jid(void **state)
|
||||
assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\""));
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='*' to='buddy1@localhost/mobile' type='get'>"
|
||||
"<iq id='prof_caps_4' to='buddy1@localhost/mobile' type='get'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>"
|
||||
"</iq>"
|
||||
));
|
||||
|
||||
// Respond to ping result regardless of id
|
||||
stbbr_for_query("urn:xmpp:ping",
|
||||
"<iq from='buddy1@localhost/mobile' to='stabber@localhost' type='result'/>"
|
||||
stbbr_for_id("prof_ping_5",
|
||||
"<iq from='buddy1@localhost/mobile' to='stabber@localhost' id='prof_ping_5' type='result'/>"
|
||||
);
|
||||
|
||||
prof_input("/ping buddy1@localhost/mobile");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='*' type='get' to='buddy1@localhost/mobile'>"
|
||||
"<iq id='prof_ping_5' type='get' to='buddy1@localhost/mobile'>"
|
||||
"<ping xmlns='urn:xmpp:ping'/>"
|
||||
"</iq>"
|
||||
));
|
||||
assert_true(prof_output_regex("Ping response from buddy1@localhost/mobile"));
|
||||
assert_true(prof_output_exact("Ping response from buddy1@localhost/mobile"));
|
||||
}
|
||||
|
||||
void ping_jid_not_supported(void **state)
|
||||
{
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#info",
|
||||
"<iq to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
|
||||
stbbr_for_id("prof_caps_4",
|
||||
"<iq id='prof_caps_4' to='stabber@localhost/profanity' type='result' from='buddy1@localhost/mobile'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='>"
|
||||
"<identity category='client' type='console' name='Profanity0.6.0'/>"
|
||||
"<feature var='http://jabber.org/protocol/disco#info'/>"
|
||||
@@ -164,7 +160,7 @@ void ping_jid_not_supported(void **state)
|
||||
assert_true(prof_output_exact("Buddy1 (mobile) is online, \"I'm here\""));
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='*' to='buddy1@localhost/mobile' type='get'>"
|
||||
"<iq id='prof_caps_4' to='buddy1@localhost/mobile' type='get'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#LpT2xs3nun7jC2sq4gg3WRDQFZ4='/>"
|
||||
"</iq>"
|
||||
));
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -12,16 +16,15 @@ presence_online(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set online");
|
||||
|
||||
assert_true(prof_output_exact("Status set to online (priority 0), \"online\"."));
|
||||
prof_input("/online");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<status>online</status>"
|
||||
"<presence id='prof_presence_3'>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to online (priority 0)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -29,16 +32,16 @@ presence_online_with_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set online \"Hi there\"");
|
||||
|
||||
assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\"."));
|
||||
prof_input("/online \"Hi there\"");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<status>Hi there</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to online (priority 0), \"Hi there\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -46,17 +49,16 @@ presence_away(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set away");
|
||||
|
||||
assert_true(prof_output_exact("Status set to away (priority 0), \"away\"."));
|
||||
prof_input("/away");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>away</show>"
|
||||
"<status>away</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to away (priority 0)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -64,17 +66,17 @@ presence_away_with_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set away \"I'm not here for a bit\"");
|
||||
|
||||
assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\"."));
|
||||
prof_input("/away \"I'm not here for a bit\"");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>away</show>"
|
||||
"<status>I'm not here for a bit</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to away (priority 0), \"I'm not here for a bit\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -82,17 +84,16 @@ presence_xa(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set xa");
|
||||
|
||||
assert_true(prof_output_exact("Status set to xa (priority 0), \"xa\"."));
|
||||
prof_input("/xa");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>xa</show>"
|
||||
"<status>xa</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to xa (priority 0)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -100,17 +101,17 @@ presence_xa_with_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set xa \"Gone to the shops\"");
|
||||
|
||||
assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\"."));
|
||||
prof_input("/xa \"Gone to the shops\"");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>xa</show>"
|
||||
"<status>Gone to the shops</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to xa (priority 0), \"Gone to the shops\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -118,17 +119,16 @@ presence_dnd(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set dnd");
|
||||
|
||||
assert_true(prof_output_exact("Status set to dnd (priority 0), \"dnd\"."));
|
||||
prof_input("/dnd");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>dnd</show>"
|
||||
"<status>dnd</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to dnd (priority 0)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -136,17 +136,17 @@ presence_dnd_with_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set dnd \"Working\"");
|
||||
|
||||
assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\"."));
|
||||
prof_input("/dnd \"Working\"");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>dnd</show>"
|
||||
"<status>Working</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to dnd (priority 0), \"Working\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -154,17 +154,16 @@ presence_chat(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set chat");
|
||||
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0), \"chat\"."));
|
||||
prof_input("/chat");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>chat</show>"
|
||||
"<status>chat</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0)"));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -172,17 +171,17 @@ presence_chat_with_message(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set chat \"Free to talk\"");
|
||||
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
|
||||
prof_input("/chat \"Free to talk\"");
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>chat</show>"
|
||||
"<status>Free to talk</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -192,14 +191,14 @@ presence_set_priority(void **state)
|
||||
|
||||
prof_input("/priority 25");
|
||||
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<priority>25</priority>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -208,24 +207,24 @@ presence_includes_priority(void **state)
|
||||
prof_connect();
|
||||
|
||||
prof_input("/priority 25");
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<priority>25</priority>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
|
||||
prof_input("/status set chat \"Free to talk\"");
|
||||
assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\"."));
|
||||
prof_input("/chat \"Free to talk\"");
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_5'>"
|
||||
"<priority>25</priority>"
|
||||
"<show>chat</show>"
|
||||
"<status>Free to talk</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
assert_true(prof_output_exact("Status set to chat (priority 25), \"Free to talk\"."));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -233,26 +232,26 @@ presence_keeps_status(void **state)
|
||||
{
|
||||
prof_connect();
|
||||
|
||||
prof_input("/status set chat \"Free to talk\"");
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
|
||||
prof_input("/chat \"Free to talk\"");
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_4'>"
|
||||
"<show>chat</show>"
|
||||
"<status>Free to talk</status>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
assert_true(prof_output_exact("Status set to chat (priority 0), \"Free to talk\"."));
|
||||
|
||||
prof_input("/priority 25");
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
assert_true(stbbr_received(
|
||||
"<presence id='*'>"
|
||||
"<presence id='prof_presence_5'>"
|
||||
"<show>chat</show>"
|
||||
"<status>Free to talk</status>"
|
||||
"<priority>25</priority>"
|
||||
"<c hash='sha-1' xmlns='http://jabber.org/protocol/caps' ver='*' node='http://profanity-im.github.io'/>"
|
||||
"</presence>"
|
||||
));
|
||||
assert_true(prof_output_exact("Priority set to 25."));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
@@ -30,9 +34,8 @@ send_receipt_request(void **state)
|
||||
|
||||
prof_connect();
|
||||
|
||||
// Register disco#info response for capabilities query (receipts support)
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#info",
|
||||
"<iq from='buddy1@localhost/laptop' to='stabber@localhost' id='*' type='result'>"
|
||||
stbbr_for_id("prof_caps_4",
|
||||
"<iq from='buddy1@localhost/laptop' to='stabber@localhost' id='prof_caps_4' type='result'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#info' node='http://profanity-im.github.io#hAkb1xZdJV9BQpgGNw8zG5Xsals='>"
|
||||
"<identity category='client' name='Profanity 0.5.0' type='console'/>"
|
||||
"<feature var='urn:xmpp:receipts'/>"
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
void
|
||||
rooms_query(void **state)
|
||||
{
|
||||
stbbr_for_query("http://jabber.org/protocol/disco#items",
|
||||
"<iq id='*' type='result' to='stabber@localhost/profanity' from='conference.localhost'>"
|
||||
stbbr_for_id("prof_confreq_4",
|
||||
"<iq id='prof_confreq_4' type='result' to='stabber@localhost/profanity' from='conference.localhost'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#items'>"
|
||||
"<item jid='chatroom@conference.localhost' name='A chat room'/>"
|
||||
"<item jid='hangout@conference.localhost' name='Another chat room'/>"
|
||||
@@ -26,8 +30,8 @@ rooms_query(void **state)
|
||||
assert_true(prof_output_exact("chatroom@conference.localhost (A chat room)"));
|
||||
assert_true(prof_output_exact("hangout@conference.localhost (Another chat room)"));
|
||||
|
||||
assert_true(stbbr_received(
|
||||
"<iq id='*' to='conference.localhost' type='get'>"
|
||||
assert_true(stbbr_last_received(
|
||||
"<iq id='prof_confreq_4' to='conference.localhost' type='get'>"
|
||||
"<query xmlns='http://jabber.org/protocol/disco#items'/>"
|
||||
"</iq>"
|
||||
));
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stabber.h>
|
||||
#include <expect.h>
|
||||
|
||||
#include "proftest.h"
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
@@ -21,7 +21,8 @@
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <xmpp/xmpp.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "config/account.h"
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "database.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <cmocka.h>
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
void
|
||||
log_init(log_level_t filter, const char* const log_file)
|
||||
log_init(log_level_t filter, char* log_file)
|
||||
{
|
||||
}
|
||||
log_level_t
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#include <libotr/message.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "config/account.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <glib.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "xmpp/contact.h"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "prof_cmocka.h"
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "xmpp/chat_session.h"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include "prof_cmocka.h"
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user