Skip to content

Contributing

ekollof edited this page Jun 10, 2026 · 4 revisions

Contributing

Contributions are welcome — bug reports, fixes, and new features alike. Please keep changes minimal and targeted; avoid unnecessary refactoring of working code.

See also AGENTS.md in the repository for the full agent/developer reference.


Getting the source

git clone https://github.com/ekollof/xepher.git
cd xepher
git submodule update --init --recursive

Building

make            # build xmpp.so + run 123 doctests
make test       # doctests only
make clean      # remove build artifacts
make install    # install to ~/.local/share/weechat/plugins/

On BSD use gmake instead of make.

Distribution-style build (no .source embed — matches release packages):

make PACKAGE_BUILD=1 weechat-xmpp

The compiler must support C++23 (-std=c++23). GCC 12+ or Clang 15+ (Linux/macOS); Clang 13+ on BSD.


Code style

General rules

  • Follow the existing style in the file you are editing.
  • C++23 throughout — use std::string_view, std::optional, std::expected, structured bindings, std::ranges, etc.
  • Use nullptr, not NULL.
  • RAII everywhere — malloc/free/new/delete are forbidden.

Port abstraction (required for new/touched code)

Isolate libstrophe reads and WeeChat output from domain logic:

Prefer Instead of
xmpp::StanzaView (src/xmpp/stanza_view.hh) xmpp_stanza_get_*, manual child-pointer walks
Fluent stanza::spec builders (src/xmpp/node.hh, XEP .inl) xmpp_stanza_new, xmpp_stanza_add_child
weechat::UiPort weechat_printf / direct buffer output in handlers
weechat::BufferPort, LineStorePort, RenderEvent Raw nicklist/line manipulation in domain code

Handler slices live under src/xmpp/message_*.cpp and src/xmpp/iq_*.cpp. Connection handlers are thin adapters. Extend these slices when adding protocol features — do not grow monolithic .inl files with raw API calls.

Raw weechat_* and xmpp_stanza_* calls belong only in hook registration glue and port adapter implementations.

Memory management

Pattern What to use
Strophe-allocated strings (e.g. xmpp_jid_bare) xmpp_string_guard
Heap byte buffers heap_buf / make_heap_buf (see src/omemo.hh)
LMDB key strings std::string + fmt::format
WeeChat string_dyn weechat_string_dyn_free(ptr, 1) and own the raw pointer
Fixed-size arrays std::make_unique<T[]>(N)

Never let an LMDB transaction go out of scope without committing or aborting it.

WeeChat conventions

  • Hook/callback signatures must match WeeChat exactly.
  • Use UiPort for user-visible output in commands and handlers.
  • Handler functions return int: 1 = keep handler, 0 = remove.
  • Never use weechat_buffer_merge() — it merges display history and causes unexpected behaviour.
  • Buffer display: use "1" (don't auto-switch), not "auto".
  • Never reload the plugin — restart WeeChat after rebuilding.

XMPP conventions

  • Outbound: fluent stanza::spec builders only — xmpp_stanza_new() is forbidden in new code.
  • Inbound: xmpp::StanzaView for reads.
  • Pure IQ replies: src/xmpp/iq_handlers.hh (handle_version_iq, handle_time_iq, …).
  • Use xmpp_uuid_gen() for IDs; guard with xmpp_string_guard.

Commit message format

<type>: <short description>

[optional body — explain the why, not the what]

Types: feat, fix, docs, refactor, test, chore.

Keep commits atomic — one logical change per commit.


Architecture overview

Key source files

File Responsibility
src/connection/message_handler.cpp Thin adapter → src/xmpp/message_*.cpp slices
src/connection/iq_handler.cpp Thin adapter → src/xmpp/iq_*.cpp slices
src/connection/presence_handler.cpp Presence and MUC join/leave
src/connection/session_lifecycle.cpp Connect/disconnect, stream management
src/xmpp/stanza_view.cpp Inbound stanza reads
src/xmpp/iq_handlers.cpp Pure IQ reply builders
src/weechat/ui_port.cpp, buffer_port.cpp, line_store.cpp WeeChat ports
src/account.cpp Account management, LMDB cache
src/channel.cpp Chat buffer management (MUC and PM)
src/command/*.cpp WeeChat /xmpp sub-commands
src/omemo/api.cpp OMEMO encryption/decryption
src/pgp.cpp PGP encryption

Large .inl fragments are compiled via thin .cpp wrappers in subdirectories. Open the .cpp wrapper (not the .inl) to read implementation logic.

Channel types

channel::chat_type::MUC   — multi-user chat room
channel::chat_type::PM    — private (1-on-1) message
channel::chat_type::FEED  — PubSub feed buffer

MAM timestamp sentinels (important!)

Value Meaning
0 Brand new — fetch 7 days of history
-1 User deliberately closed — skip MAM; do not auto-recreate
> 0 Existing channel — fetch only messages newer than this timestamp

Testing

123 doctests run automatically at the end of make DEBUG=1 (or make test anytime). They cover handler slices, StanzaView, IQ builders, and port stubs (tests/weechat_stub.hh provides CapturingUiPort, NullUiPort, StubRuntimePort).

make test

Full WeeChat integration still requires manual testing:

  1. Build and install: make && make install
  2. Restart WeeChat (do not /plugin reload xmpp).
  3. Connect an account and exercise the changed feature.
  4. Check logs (xmpp.look.debug, xmpp.look.raw_xml_log) for protocol issues.

Manual regression checklist

  • PM buffers do not reappear after /close
  • Typing indicators show nick (not full JID) in MUC
  • OMEMO encrypts/decrypts in a PM
  • /bookmark lists bookmarks; autojoin works on reconnect
  • Plugin loads without errors (/plugin list shows xmpp)

Submitting changes

  1. Fork the repository on GitHub.
  2. Create a branch: git checkout -b fix/my-bug or feat/my-feature.
  3. Make focused, atomic commits.
  4. Run make (doctests + build).
  5. Test manually in WeeChat.
  6. Open a pull request against master.

Cutting a release

See the Releasing wiki page. Pushing a v* tag triggers GitHub Actions to build and attach packages automatically.

Clone this wiki locally