merge c with pp

v2
Tony Olagbaiye 3 years ago
commit 5377445423
No known key found for this signature in database
GPG Key ID: 9E2FF3BDEBDFC910

@ -16,7 +16,7 @@ PACKAGES=(
make # Deps with makefiles make # Deps with makefiles
cmake # Deps with cmake cmake # Deps with cmake
doctest # Testing doctest # Testing
gcc-toolchain@11 # Compilation gcc-toolchain@10 # Compilation
pkg-config # Deps configuration and configuration of deps deps pkg-config # Deps configuration and configuration of deps deps
patchelf # Fix linkage (guix) patchelf # Fix linkage (guix)
bear # Generate compile_commands.json for language servers bear # Generate compile_commands.json for language servers

3
.gitattributes vendored

@ -0,0 +1,3 @@
# Github language display
*.h linguist-language=C
*.inc linguist-language=C

@ -0,0 +1,4 @@
language: c
script: make
compiler:
- gcc

@ -38,11 +38,44 @@ LDLIBS=-lstrophe \
PREFIX ?= /usr/local PREFIX ?= /usr/local
LIBDIR ?= $(PREFIX)/lib LIBDIR ?= $(PREFIX)/lib
HDRS=plugin.hh HDRS=plugin.hh \
SRCS=plugin.cpp plugin.h \
DEPS=deps/diff/libdiff.a account.h \
buffer.h \
channel.h \
command.h \
completion.h \
config.h \
connection.h \
input.h \
message.h \
omemo.h \
pgp.h \
user.h \
util.h \
xmpp/stanza.h \
SRCS=plugin.cpp \
account.c \
buffer.c \
channel.c \
command.c \
completion.c \
config.c \
connection.c \
input.c \
message.c \
omemo.c \
pgp.c \
user.c \
util.c \
xmpp/presence.c \
xmpp/iq.c \
DEPS=deps/diff/libdiff.a \
TSTS=$(patsubst %.cpp,tests/%.cc,$(SRCS)) tests/main.cc TSTS=$(patsubst %.cpp,tests/%.cc,$(SRCS)) tests/main.cc
OBJS=$(patsubst %.cpp,.%.o,$(SRCS)) OBJS=$(patsubst %.cpp,.%.o,$(patsubst %.c,.%.o,$(patsubst xmpp/%.c,xmpp/.%.o,$(SRCS))))
JOBS=$(patsubst tests/%.cc,tests/.%.o,$(TSTS)) JOBS=$(patsubst tests/%.cc,tests/.%.o,$(TSTS))
all: weechat-xmpp all: weechat-xmpp
@ -57,6 +90,12 @@ xmpp.so: $(OBJS) $(DEPS) $(HDRS)
.%.o: %.cpp .%.o: %.cpp
@$(CXX) $(CPPFLAGS) -c $< -o $@ @$(CXX) $(CPPFLAGS) -c $< -o $@
.%.o: %.c
@$(CC) $(CFLAGS) -c $< -o $@
xmpp/.%.o: xmpp/%.c
@$(CC) $(CFLAGS) -c $< -o $@
tests/.%.o: tests/%.cc tests/.%.o: tests/%.cc
@$(CXX) $(CPPFLAGS) -c $< -o $@ @$(CXX) $(CPPFLAGS) -c $< -o $@

@ -0,0 +1,145 @@
✗: No support
⧗: Planned support
✓: Partial support
?: Supported, but status not specified
✓: Complete support
🕇: Removed support
☠: Client will never support this XEP
+-------------------------------------------------------------------------+------------------------+---------------+------+-------+----------+-------+--------+-------+
| XEP | Bruno the Jabber™ Bear | Conversations | Dino | Gajim | Monal IM | Movim | Poezio | yaxim |
+-------------------------------------------------------------------------+------------------------+---------------+------+-------+----------+-------+--------+-------+
| 0004: Data Forms(Final) | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0012: Last Activity(Final) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ |
| 0027: Current Jabber OpenPGP Usage(Obsolete) | ✗ | ✓ | ✓ | ✓ | ☠ | ✗ | ✓ | ✗ |
| 0030: Service Discovery(Final) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0033: Extended Stanza Addressing(Draft) | ✗ | ✗ | ✗ | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0045: Multi-User Chat(Draft) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0047: In-Band Bytestreams(Final) | ✗ | ✗ | ? | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0048: Bookmarks(Deprecated) | ✓ | ✓ | ? | ✓ | ⧗ | ✓ | ✓ | ✓ |
| 0049: Private XML Storage(Active) | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 0050: Ad-Hoc Commands(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0054: vcard-temp(Active) | ✗ | ✓ | ✓ | ✓ | ☠ | ✓ | ✓ | ✗ |
| 0055: Jabber Search(Active) | ✗ | ✗ | ✗ | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0059: Result Set Management(Draft) | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ |
| 0060: Publish-Subscribe(Draft) | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0065: SOCKS5 Bytestreams(Draft) | ✗ | ✗ | ✗ | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0066: Out of Band Data(Draft) | ✓ | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ |
| 0070: Verifying HTTP Requests via XMPP(Draft) | ✗ | ✗ | ✗ | ✓ | ☠ | ✓ | ✓ | ✗ |
| 0071: XHTML-IM(Deprecated) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0077: In-Band Registration(Final) | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0080: User Location(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0082: XMPP Date and Time Profiles(Active) | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0083: Nested Roster Groups(Active) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0084: User Avatar(Draft) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0085: Chat State Notifications(Final) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0091: Legacy Delayed Delivery(Obsolete) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
| 0092: Software Version(Draft) | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0095: Stream Initiation(Deprecated) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0100: Gateway Interaction(Active) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0106: JID Escaping(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0107: User Mood(Draft) | ✗ | ✗ | ✗ | ✓ | ☠ | ✓ | ✓ | ✗ |
| 0108: User Activity(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0115: Entity Capabilities(Draft) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0118: User Tune(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0144: Roster Item Exchange(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0145: Annotations(Active) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0146: Remote Controlling Clients(Obsolete) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0147: XMPP URI Scheme Query Components(Active) | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✓ |
| 0153: vCard-Based Avatars(Active) | ✗ | ✓ | ? | ✓ | ☠ | ✓ | ✓ | ✗ |
| 0156: Discovering Alternative XMPP Connection Methods(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0157: Contact Addresses for XMPP Services(Active) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0158: CAPTCHA Forms(Draft) | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
| 0162: Best Practices for Roster and Subscription Management(Deferred) | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
| 0163: Personal Eventing Protocol(Draft) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0166: Jingle(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0167: Jingle RTP Sessions(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0172: User Nickname(Draft) | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0174: Serverless Messaging(Final) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0175: Best Practices for Use of SASL ANONYMOUS(Active) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ |
| 0176: Jingle ICE-UDP Transport Method(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0178: Best Practices for Use of SASL EXTERNAL with Certificates(Active) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ |
| 0184: Message Delivery Receipts(Draft) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0191: Blocking Command(Draft) | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
| 0196: User Gaming(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 0198: Stream Management(Draft) | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |
| 0199: XMPP Ping(Final) | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 0200: Stanza Encryption(Deferred) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0202: Entity Time(Final) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ |
| 0203: Delayed Delivery(Final) | ✓ | ✗ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 0209: Metacontacts(Deferred) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0215: External Service Discovery(Deferred) | ✗ | ✓ | ? | ✗ | ⧗ | ✓ | ✗ | ✗ |
| 0221: Data Forms Media Element(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0222: Persistent Storage of Public Data via PubSub(Active) | ✗ | ✗ | ? | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0223: Persistent Storage of Private Data via PubSub(Active) | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
| 0224: Attention(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ |
| 0231: Bits of Binary(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0234: Jingle File Transfer(Deferred) | ✗ | ✓ | ? | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0237: Roster Versioning(Obsolete) | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
| 0245: The /me Command(Active) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0249: Direct MUC Invitations(Draft) | ✓ | ✓ | ? | ✓ | ⧗ | ✗ | ✓ | ✓ |
| 0256: Last Activity in Presence(Draft) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0257: Client Certificate Management for SASL EXTERNAL(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 0258: Security Labels in XMPP(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0260: Jingle SOCKS5 Bytestreams Transport Method(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0261: Jingle In-Band Bytestreams Transport Method(Draft) | ✗ | ✓ | ✓ | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0277: Microblogging over XMPP(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0280: Message Carbons(Experimental) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0284: Shared XML Editing(Deferred) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0286: Mobile Considerations on LTE Networks(Active) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| 0292: vCard4 Over XMPP(Deferred) | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | ✗ | ✗ |
| 0293: Jingle RTP Feedback Negotiation(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0294: Jingle RTP Header Extensions Negotiation(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0296: Best Practices for Resource Locking(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 0297: Stanza Forwarding(Draft) | ✓ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ | ✓ |
| 0300: Use of Cryptographic Hash Functions in XMPP(Draft) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0306: Extensible Status Conditions for Multi-User Chat(Deferred) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0308: Last Message Correction(Draft) | ✓ | ✓ | ? | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0313: Message Archive Management(Experimental) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0319: Last User Interaction in Presence(Draft) | ✗ | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ |
| 0320: Use of DTLS-SRTP in Jingle Sessions(Draft) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0330: Pubsub Subscription(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0333: Chat Markers(Deferred) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ |
| 0334: Message Processing Hints(Deferred) | ✗ | ✗ | ? | ✓ | ✗ | ✓ | ✓ | ✗ |
| 0338: Jingle Grouping Framework(Draft) | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0339: Source-Specific Media Attributes in Jingle(Draft) | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0343: Signaling WebRTC datachannels in Jingle(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0352: Client State Indication(Draft) | ✓ | ✓ | ✗ | ✗ | ✓ | ✗ | ✓ | ✓ |
| 0353: Jingle Message Initiation(Deferred) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0357: Push Notifications(Deferred) | ✓ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ |
| 0359: Unique and Stable Stanza IDs(Deferred) | ✓ | ✗ | ? | ✓ | ✗ | ✓ | ✗ | ✓ |
| 0363: HTTP File Upload(Draft) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 0364: Current Off-the-Record Messaging Usage(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 0367: Message Attaching(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0368: SRV records for XMPP over TLS(Draft) | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ |
| 0369: Mediated Information eXchange (MIX)(Experimental) | ✗ | ✗ | ✗ | ✗ | ⧗ | ✗ | ✗ | ✗ |
| 0372: References(Experimental) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0373: OpenPGP for XMPP(Experimental) | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0374: OpenPGP for XMPP Instant Messaging(Deferred) | ✗ | ✗ | ✗ | ✗ | ⧗ | ✗ | ✗ | ✗ |
| 0377: Spam Reporting(Experimental) | ✗ | ✓ | ✗ | ✓ | ☠ | ✗ | ✗ | ✗ |
| 0378: OTR Discovery(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| 0379: Pre-Authenticated Roster Subscription(Deferred) | ✓ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ |
| 0380: Explicit Message Encryption(Deferred) | ✗ | ✗ | ✓ | ✓ | ⧗ | ✓ | ✓ | ✗ |
| 0384: OMEMO Encryption(Experimental) | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
| 0385: Stateless Inline Media Sharing (SIMS)(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0386: Bind 2.0(Deferred) | ✗ | ✗ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0390: Entity Capabilities 2.0(Deferred) | ✗ | ✗ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0391: Jingle Encrypted Transports(Deferred) | ✗ | ✓ | ? | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0392: Consistent Color Generation(Deferred) | ✓ | ✓ | ✗ | ✓ | ⧗ | ✗ | ✓ | ✓ |
| 0393: Message Styling(Draft) | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| 0396: Jingle Encrypted Transports - OMEMO(Deferred) | ✗ | ✓ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0397: Instant Stream Resumption(Deferred) | ✗ | ✗ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0398: User Avatar to vCard-Based Avatars Conversion(Deferred) | ✗ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0401: Easy User Onboarding(Deferred) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
| 0402: PEP Native Bookmarks(Draft) | ✗ | ✗ | ✗ | ✗ | ⧗ | ✓ | ✗ | ✗ |
| 0409: IM Routing-NG(Deferred) | ✗ | ✗ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0410: MUC Self-Ping (Schrödinger's Chat)(Draft) | ✓ | ✓ | ? | ✗ | ☠ | ✗ | ✗ | ✓ |
| 0411: Bookmarks Conversion(Draft) | ✗ | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ |
| 0420: Stanza Content Encryption(Experimental) | ✗ | ✗ | ✗ | ✗ | ☠ | ✗ | ✗ | ✗ |
| 0422: Message Fastening(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0423: XMPP Compliance Suites 2020(Obsolete) | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
| 0424: Message Retraction(Deferred) | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |
| 0441: Message Archive Management Preferences(Experimental) | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ |
| 0447: Stateless file sharing(Experimental) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0449: Stickers(Experimental) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| 0454: OMEMO Media sharing(Experimental) | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
+-------------------------------------------------------------------------+------------------------+---------------+------+-------+----------+-------+--------+-------+

@ -0,0 +1,177 @@
#+TITLE: weechat-xmpp
#+AUTHOR: Tony Olagbaiye
#+EMAIL: frony0@gmail.com
#+DATE: 2018-05-09
#+DESCRIPTION: Weechat plugin for XMPP
#+KEYWORDS: weechat xmpp c api
#+LANGUAGE: en
#+OPTIONS: H:3 num:nil toc:nil \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
#+OPTIONS: TeX:t LaTeX:nil skip:nil d:nil todo:t pri:t tags:not-in-toc
#+EXPORT_EXCLUDE_TAGS: exclude
#+STARTUP: showall
[[https://travis-ci.org/bqv/weechat-xmpp][file:https://api.travis-ci.org/bqv/weechat-xmpp.svg?branch=master]]
[[https://coveralls.io/github/bqv/weechat-xmpp?branch=master][file:https://coveralls.io/repos/github/bqv/weechat-xmpp/badge.svg?branch=master]]
[[https://github.com/bqv/weechat-xmpp/issues][file:https://img.shields.io/github/issues/bqv/weechat-xmpp.svg]]
[[https://github.com/bqv/weechat-xmpp/issues?q=is:issue+is:closed][file:https://img.shields.io/github/issues-closed/bqv/weechat-xmpp.svg]]
[[https://github.com/bqv/weechat-xmpp/blob/master/LICENSE][file:https://img.shields.io/github/license/bqv/weechat-xmpp.svg]]
[[https://github.com/bqv/weechat-extras/][file:https://img.shields.io/badge/weechat--extras-xmpp-blue.svg]]
[[https://github.com/bqv/weechat-extras/][file:https://inverse.chat/badge.svg?room=weechat@muc.xa0.uk]]
| Status: | XMPP for power users and digital masochists |
| Location: | [[http://github.com/bqv/weechat-xmpp]] |
| Version: | 0.1.1 |
| Disclaimer: | I'm lazy and unashamedly clinically insane |
* Description
A weechat plugin in C to extend the chat client to
support XMPP and a currently minimal but ideally maximal
set of XEPs.
My priority here is to have an android-available XMPP client
that hides as little as possible from the user. To use this with
android, set up a relay (`/relay`) and see weechat-android.
I'm gonna rewrite this in C++ at some point when I have a feel
for the full behaviour of an average client.
* Usage
1. Start with =/xmpp add=, use =/help xmpp= for instructions.
2. Use =/xmpp connect <account-name>= with the name set at
add-time.
* Installing
Place xmpp.so in the appropriate place for weechat plugins.
* Dependencies
- libstrophe (dynamic, dependency)
- libxml2 (dynamic, dependency)
- libsignal-protocol-c (dynamic, dependency)
- rnp (dynamic, dependency)
- weechat (>= v3.0)
.. or just use the guix spec in .envrc
* Building
#+begin_src sh
git clone git://github.com/bqv/weechat-xmpp.git
cd weechat-xmpp
git submodule update --init --recursive
make
make install
#+end_src
Do *NOT* run make install as root, it installs the plugin to your
local weechat plugins directory
* Development
I use emacs for development of this, although I am also a fan of vim.
My debug build process involves static analysis with clang and cppcheck,
and dynamic analysis with address-sanitizer and leak-sanitizer.
My debug evaluation process involves gdb/mi run with the arguments
=-ex "handle SIGPIPE nostop noprint pass" --args weechat -a 2>asan.log=
since part of weechat and it's default plugins use SIGPIPE as control.
I have no real requests for style of pull requests besides a wish that
you keep vaguely to the indentation style I use for this project.
Happy coding!
* Tasks
** DONE [#A] Implement basic functionality (milestone v0.1)
* [X] Connecting
* [X] Pretty-printing stanzas
* [X] Receiveing and formatting PMs to a dedicated buffer
* [X] Responding to PMs and formatting responses
* [X] Opening PMs (/chat)
** TODO [#A] Implement essential functionality (milestone v0.2)
* [X] Opening PMs with initial message
* [-] OOB messages
* [X] Single media on a line
* [ ] Multiple media inline (protocol?)
* [X] Buffer autoswitch on enter/open
* [X] Handle open/enter jids with a resource without breaking
* [X] Allow /close without crashing
* [ ] [#B] Handle wide errors gracefully
* [ ] [#B] Event-driven MUC entrance
* [ ] [#C] XMPP Ping (xep-199)
* [X] [#A] Highlight
* [-] MUCs
* [X] Opening (/enter)
* [ ] [#B] Leave on /close
* [X] Receiving
* [X] Sending
* [X] With /msg
* [-] [#B] Edits
* [X] [#B] Displaying
* [X] [#B] Tagging
* [ ] [#B] Making
* [X] [#C] Diff highlighting
* [ ] [#B] Handle errors gracefully
* [X] [#B] Presence/nicklist
* [X] [#B] Enters
* [X] [#B] Leaves
* [X] [#B] Tracking
* [X] [#B] Set/show topic
* [-] OMEMO (libsignal-protocol-c)
* [-] Presence
* [X] Disco
* [X] Disco response
* [-] Key Generation / storage (lmdb)
* [X] Generation
* [?] Storage
* [-] Announce
* [X] Device ID
* [ ] Bundles
* [ ] Messages
* [ ] [#C] MUC PMs
* [X] [#A] Send typing notifications
* [X] [#A] Recv typing notifications
* [X] [#C] Read receipts
* [X] Chat Markers (XEP-0333)
* [X] Composing
* [X] Paused
* [?] Active
* [ ] Inactive
* [ ] Gone
* [X] Message Delivery (XEP-0184)
* [X] Message Carbons
* [ ] Service Disco
* [X] MAM Fetching
* [-] Bookmarks / Roster
* [X] Autojoin bookmarks
* [ ] Add bookmarks
* [ ] Delete bookmarks
* [ ] Roster
* [ ] OTR (libotr)
* [X] PGP (rnpgp)
* [X] Use keyrings (e.g. exported from gnupg)
* [X] Presence
* [X] Decryption
* [X] Encryption
* [X] Custom set/clear key (/pgp)
* [ ] OOB data and media
* [ ] Room Explorer (https://search.jabber.network/docs/api)
** TODO [#C] Implement completion engine (milestone v0.3)
** TODO [#D] Close all issues (milestone v1.0)
* Contributing
*Your contributions are always welcome!*
Please submit a pull request or create an issue
to add a new or missing feature.
* Testemonials
"Weechat-Strophe - for the discerning dual IRCer XMPPer" -- [[github.com/janicez][Ellenor et al Bjornsdottir]]
* License
weechat-xmpp is licensed under the Mozilla Public
License Version 2.0 available [[https://www.mozilla.org/en-US/MPL/2.0/][here]] and in LICENSE.

@ -0,0 +1,711 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <strophe.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <libxml/xmlwriter.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "config.h"
#include "input.h"
#include "omemo.h"
#include "account.h"
#include "connection.h"
#include "user.h"
#include "channel.h"
#include "buffer.h"
struct t_account *accounts = NULL;
struct t_account *last_account = NULL;
char *account_options[ACCOUNT_NUM_OPTIONS][2] =
{ { "jid", "" },
{ "password", "" },
{ "tls", "normal" },
{ "nickname", "" },
{ "autoconnect", "" },
{ "resource", "" },
{ "status", "probably about to segfault" },
{ "pgp_pubring_path", "${weechat_data_dir}/pubring.gpg" },
{ "pgp_secring_path", "${weechat_data_dir}/secring.gpg" },
{ "pgp_keyid", "" },
};
struct t_account *account__search(const char *name)
{
struct t_account *ptr_account;
if (!name)
return NULL;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
if (strcmp(ptr_account->name, name) == 0)
return ptr_account;
}
/* account not found */
return NULL;
}
struct t_account *account__casesearch (const char *name)
{
struct t_account *ptr_account;
if (!name)
return NULL;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
if (weechat_strcasecmp (ptr_account->name, name) == 0)
return ptr_account;
}
/* account not found */
return NULL;
}
int account__search_option(const char *option_name)
{
int i;
if (!option_name)
return -1;
for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
{
if (weechat_strcasecmp(account_options[i][0], option_name) == 0)
return i;
}
/* account option not found */
return -1;
}
struct t_account_device *account__search_device(struct t_account *account, int id)
{
struct t_account_device *ptr_device;
if (!account)
return NULL;
for (ptr_device = account->devices; ptr_device;
ptr_device = ptr_device->next_device)
{
if (ptr_device->id == id)
return ptr_device;
}
return NULL;
}
void account__add_device(struct t_account *account,
struct t_account_device *device)
{
struct t_account_device *new_device;
new_device = account__search_device(account, device->id);
if (!new_device)
{
new_device = malloc(sizeof(*new_device));
new_device->id = device->id;
new_device->name = strdup(device->name);
new_device->prev_device = account->last_device;
new_device->next_device = NULL;
if (account->last_device)
(account->last_device)->next_device = new_device;
else
account->devices = new_device;
account->last_device = new_device;
}
}
void account__free_device(struct t_account *account, struct t_account_device *device)
{
struct t_account_device *new_devices;
if (!account || !device)
return;
/* remove device from devices list */
if (account->last_device == device)
account->last_device = device->prev_device;
if (device->prev_device)
{
(device->prev_device)->next_device = device->next_device;
new_devices = account->devices;
}
else
new_devices = device->next_device;
if (device->next_device)
(device->next_device)->prev_device = device->prev_device;
/* free device data */
if (device->name)
free(device->name);
free(device);
account->devices = new_devices;
}
void account__free_device_all(struct t_account *account)
{
while (account->devices)
account__free_device(account, account->devices);
}
struct t_account_mam_query *account__add_mam_query(struct t_account *account,
struct t_channel *channel,
const char *id,
time_t *start, time_t *end)
{
struct t_account_mam_query *mam_query;
if (!(mam_query = account__mam_query_search(account, id)))
{
mam_query = malloc(sizeof(struct t_account_mam_query));
mam_query->id = strdup(id);
mam_query->with = strdup(channel->id);
mam_query->has_start = start != NULL;
if (mam_query->has_start)
mam_query->start = *start;
mam_query->has_end = end != NULL;
if (mam_query->has_end)
mam_query->end = *end;
mam_query->prev_mam_query = account->last_mam_query;
mam_query->next_mam_query = NULL;
if (account->last_mam_query)
(account->last_mam_query)->next_mam_query = mam_query;
else
account->mam_queries = mam_query;
account->last_mam_query = mam_query;
}
return mam_query;
}
struct t_account_mam_query *account__mam_query_search(struct t_account *account,
const char *id)
{
struct t_account_mam_query *ptr_mam_query;
if (!account || !id)
return NULL;
for (ptr_mam_query = account->mam_queries; ptr_mam_query;
ptr_mam_query = ptr_mam_query->next_mam_query)
{
if (weechat_strcasecmp(ptr_mam_query->id, id) == 0)
return ptr_mam_query;
}
return NULL;
}
void account__mam_query_free(struct t_account *account,
struct t_account_mam_query *mam_query)
{
struct t_account_mam_query *new_mam_queries;
if (!account || !mam_query)
return;
/* remove mam_query from mam_queries list */
if (account->last_mam_query == mam_query)
account->last_mam_query = mam_query->prev_mam_query;
if (mam_query->prev_mam_query)
{
(mam_query->prev_mam_query)->next_mam_query = mam_query->next_mam_query;
new_mam_queries = account->mam_queries;
}
else
new_mam_queries = mam_query->next_mam_query;
if (mam_query->next_mam_query)
(mam_query->next_mam_query)->prev_mam_query = mam_query->prev_mam_query;
/* free mam_query data */
if (mam_query->id)
free(mam_query->id);
if (mam_query->with)
free(mam_query->with);
free(mam_query);
account->mam_queries = new_mam_queries;
}
void account__mam_query_free_all(struct t_account *account)
{
while (account->mam_queries)
account__mam_query_free(account, account->mam_queries);
}
void account__log_emit_weechat(void *const userdata, const xmpp_log_level_t level,
const char *const area, const char *const msg)
{
struct t_account *account = (struct t_account*)userdata;
static const char *log_level_name[4] = {"debug", "info", "warn", "error"};
const char *tags = level > XMPP_LEVEL_DEBUG ? "no_log" : NULL;
char *xml;
if ((level == XMPP_LEVEL_DEBUG) && ((xml = strchr(msg, '<')) != NULL))
{
FILE *nullfd = fopen("/dev/null", "w+");
xmlGenericErrorContext = nullfd;
const char *header = strndup(msg, xml - msg);
xmlDocPtr doc = xmlRecoverMemory(xml, strlen(xml));
if (doc == NULL) {
weechat_printf(
account ? account->buffer : NULL,
"xml: Error parsing the xml document");
fclose(nullfd);
return;
}
xmlNodePtr root = xmlDocGetRootElement(doc);
const char *tag = root ? (const char*)root->name : "";
const char *colour = weechat_color("blue");
if (weechat_strcasecmp(tag, "message"))
{
colour = weechat_color("green");
}
else if (weechat_strcasecmp(tag, "presence"))
{
colour = weechat_color("yellow");
}
else if (weechat_strcasecmp(tag, "iq"))
{
colour = weechat_color("red");
}
xmlChar *buf = malloc(strlen(xml) * 2);
if (buf == NULL) {
weechat_printf(
account ? account->buffer : NULL,
"xml: Error allocating the xml buffer");
fclose(nullfd);
return;
}
int size = -1;
xmlDocDumpFormatMemory(doc, &buf, &size, 1);
if (size <= 0) {
weechat_printf(
account ? account->buffer : NULL,
"xml: Error formatting the xml document");
fclose(nullfd);
return;
}
char **lines = weechat_string_split((char*)buf, "\r\n", NULL,
0, 0, &size);
if (lines[size-1][0] == 0)
lines[--size] = 0;
weechat_printf_date_tags(
account ? account->buffer : NULL,
0, tags,
_("%s%s (%s): %s"),
weechat_prefix("network"), area,
log_level_name[level], header);
for (int i = 1; i < size; i++)
weechat_printf_date_tags(
account ? account->buffer : NULL,
0, tags,
_("%s%s"), colour, lines[i]);
weechat_string_free_split(lines);
fclose(nullfd);
}
else
{
weechat_printf_date_tags(
account ? account->buffer : NULL,
0, tags,
_("%s%s (%s): %s"),
weechat_prefix("network"), area,
log_level_name[level], msg);
}
}
struct t_account *account__alloc(const char *name)
{
struct t_account *new_account;
int i, length;
char *option_name;
if (account__casesearch(name))
return NULL;
/* alloc memory for new account */
new_account = malloc(sizeof(*new_account));
if (!new_account)
{
weechat_printf(NULL,
_("%s%s: error when allocating new account"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return NULL;
}
/* add new account to queue */
new_account->prev_account = last_account;
new_account->next_account = NULL;
if (last_account)
last_account->next_account = new_account;
else
accounts = new_account;
last_account = new_account;
/* set name */
new_account->name = strdup(name);
/* internal vars */
new_account->reloading_from_config = 0;
new_account->is_connected = 0;
new_account->disconnected = 0;
new_account->current_retry = 0;
new_account->reconnect_delay = 0;
new_account->reconnect_start = 0;
new_account->logger.handler = &account__log_emit_weechat;
new_account->logger.userdata = new_account;
new_account->context = xmpp_ctx_new(NULL, &new_account->logger);
new_account->connection = NULL;
new_account->buffer = NULL;
new_account->buffer_as_string = NULL;
new_account->omemo = NULL;
new_account->devices = NULL;
new_account->last_device = NULL;
new_account->mam_queries = NULL;
new_account->last_mam_query = NULL;
new_account->users = NULL;
new_account->last_user = NULL;
new_account->channels = NULL;
new_account->last_channel = NULL;
/* create options with null value */
for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
{
new_account->options[i] = NULL;
length = strlen(new_account->name) + 1 +
strlen(account_options[i][0]) +
512 + /* inherited option name(xmpp.account_default.xxx) */
1;
option_name = malloc(length);
if (option_name)
{
snprintf(option_name, length, "%s.%s << xmpp.account_default.%s",
new_account->name,
account_options[i][0],
account_options[i][0]);
new_account->options[i] = config__account_new_option(
config_file,
config_section_account,
i,
option_name,
account_options[i][1],
account_options[i][1],
0,
&config__account_check_value_cb,
account_options[i][0],
NULL,
&config__account_change_cb,
account_options[i][0],
NULL);
config__account_change_cb(account_options[i][0], NULL,
new_account->options[i]);
free(option_name);
}
}
return new_account;
}
void account__free_data(struct t_account *account)
{
//int i;
if (!account)
return;
/* free linked lists */
/*
for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++)
{
account__outqueue_free_all(account, i);
}
xmpp_redirect_free_all(account);
xmpp_notify_free_all(account);
*/
account__free_device_all(account);
account__mam_query_free_all(account);
channel__free_all(account);
user__free_all(account);
/* free hashtables */
/*
weechat_hashtable_free(account->join_manual);
weechat_hashtable_free(account->join_channel_key);
weechat_hashtable_free(account->join_noswitch);
*/
/* close xmpp context */
if (account->connection)
xmpp_conn_release(account->connection);
if (account->context)
xmpp_ctx_free(account->context);
/* free account data */
//for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
//{
// if (account->options[i])
// weechat_config_option_free(account->options[i]);
//}
if (account->name)
free(account->name);
if (account->buffer_as_string)
free(account->buffer_as_string);
if (account->omemo)
omemo__free(account->omemo);
//channel__free_all(account);
//user__free_all(account);
}
void account__free(struct t_account *account)
{
struct t_account *new_accounts;
if (!account)
return;
/*
* close account buffer (and all channels/privates)
* (only if we are not in a /upgrade, because during upgrade we want to
* keep connections and closing account buffer would disconnect from account)
*/
if (account->buffer)
weechat_buffer_close(account->buffer);
/* remove account from queue */
if (last_account == account)
last_account = account->prev_account;
if (account->prev_account)
{
(account->prev_account)->next_account = account->next_account;
new_accounts = accounts;
}
else
new_accounts = account->next_account;
if (account->next_account)
(account->next_account)->prev_account = account->prev_account;
account__free_data(account);
free(account);
accounts = new_accounts;
}
void account__free_all()
{
/* for each account in memory, remove it */
while (accounts)
{
account__free(accounts);
}
}
void account__disconnect(struct t_account *account, int reconnect)
{
(void) reconnect;
struct t_channel *ptr_channel;
if (account->is_connected)
{
/*
* remove all nicks and write disconnection message on each
* channel/private buffer
*/
user__free_all(account);
weechat_nicklist_remove_all(account->buffer);
for (ptr_channel = account->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
weechat_nicklist_remove_all(ptr_channel->buffer);
weechat_printf(
ptr_channel->buffer,
_("%s%s: disconnected from account"),
weechat_prefix("network"), WEECHAT_XMPP_PLUGIN_NAME);
}
/* remove away status on account buffer */
//weechat_buffer_set(account->buffer, "localvar_del_away", "");
}
account__close_connection(account);
if (account->buffer)
{
weechat_printf(
account->buffer,
_("%s%s: disconnected from account"),
weechat_prefix ("network"), WEECHAT_XMPP_PLUGIN_NAME);
}
if (reconnect)
{
if (account->current_retry++ == 0)
{
account->reconnect_delay = 5;
account->reconnect_start = time(NULL) + account->reconnect_delay;
}
account->current_retry %= 5;
}
else
{
account->current_retry = 0;
account->reconnect_delay = 0;
account->reconnect_start = 0;
}
/*
account->lag = 0;
account->lag_displayed = -1;
account->lag_check_time.tv_sec = 0;
account->lag_check_time.tv_usec = 0;
account->lag_next_check = time(NULL) +
weechat_config_integer(xmpp_config_network_lag_check);
account->lag_last_refresh = 0;
account__set_lag(account);
*/ // lag based on xmpp ping
account->disconnected = !reconnect;
/* send signal "account_disconnected" with account name */
(void) weechat_hook_signal_send("xmpp_account_disconnected",
WEECHAT_HOOK_SIGNAL_STRING, account->name);
}
void account__disconnect_all()
{
struct t_account *ptr_account;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
account__disconnect(ptr_account, 0);
}
}
struct t_gui_buffer *account__create_buffer(struct t_account *account)
{
char buffer_name[256], charset_modifier[256];
snprintf(buffer_name, sizeof(buffer_name),
"account.%s", account->name);
account->buffer = weechat_buffer_new(buffer_name,
&input__data_cb, NULL, NULL,
&buffer__close_cb, NULL, NULL);
if (!account->buffer)
return NULL;
if (!weechat_buffer_get_integer(account->buffer, "short_name_is_set"))
weechat_buffer_set(account->buffer, "short_name", account->name);
weechat_buffer_set(account->buffer, "localvar_set_type", "server");
weechat_buffer_set(account->buffer, "localvar_set_server", account->name);
weechat_buffer_set(account->buffer, "localvar_set_channel", account->name);
snprintf(charset_modifier, sizeof (charset_modifier),
"account.%s", account->name);
weechat_buffer_set(account->buffer, "localvar_set_charset_modifier",
charset_modifier);
weechat_buffer_set(account->buffer, "title",
(account->name) ? account->name : "");
weechat_buffer_set(account->buffer, "nicklist", "1");
weechat_buffer_set(account->buffer, "nicklist_display_groups", "0");
weechat_buffer_set_pointer(account->buffer, "nicklist_callback",
&buffer__nickcmp_cb);
weechat_buffer_set_pointer(account->buffer, "nicklist_callback_pointer",
account);
return account->buffer;
}
void account__close_connection(struct t_account *account)
{
if (account->connection)
{
if (xmpp_conn_is_connected(account->connection))
xmpp_disconnect(account->connection);
}
account->is_connected = 0;
}
int account__connect(struct t_account *account)
{
if (!account->buffer)
{
if (!account__create_buffer(account))
return 0;
weechat_buffer_set(account->buffer, "display", "auto");
}
account__close_connection(account);
account->is_connected =
connection__connect(account, &account->connection, account_jid(account),
account_password(account), account_tls(account));
(void) weechat_hook_signal_send("xmpp_account_connecting",
WEECHAT_HOOK_SIGNAL_STRING, account->name);
return account->is_connected;
}
int account__timer_cb(const void *pointer, void *data, int remaining_calls)
{
(void) pointer;
(void) data;
(void) remaining_calls;
struct t_account *ptr_account;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
if (ptr_account->is_connected
&& (xmpp_conn_is_connecting(ptr_account->connection)
|| xmpp_conn_is_connected(ptr_account->connection)))
connection__process(ptr_account->context, ptr_account->connection, 10);
else if (ptr_account->disconnected);
else if (ptr_account->reconnect_start > 0
&& ptr_account->reconnect_start < time(NULL))
{
account__connect(ptr_account);
}
}
return WEECHAT_RC_OK;
}

@ -0,0 +1,154 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _ACCOUNT_H_
#define _ACCOUNT_H_
extern struct t_account *accounts;
extern struct t_account *last_account;
enum t_account_option
{
ACCOUNT_OPTION_JID,
ACCOUNT_OPTION_PASSWORD,
ACCOUNT_OPTION_TLS,
ACCOUNT_OPTION_NICKNAME,
ACCOUNT_OPTION_AUTOCONNECT,
ACCOUNT_OPTION_RESOURCE,
ACCOUNT_OPTION_STATUS,
ACCOUNT_OPTION_PGP_PUBRING_PATH,
ACCOUNT_OPTION_PGP_SECRING_PATH,
ACCOUNT_OPTION_PGP_KEYID,
ACCOUNT_NUM_OPTIONS,
};
#define account__option_string(account, option) \
weechat_config_string(account->options[ACCOUNT_OPTION_ ## option])
#define account__option_integer(account, option) \
weechat_config_integer(account->options[ACCOUNT_OPTION_ ## option])
#define account__option_boolean(account, option) \
weechat_config_boolean(account->options[ACCOUNT_OPTION_ ## option])
#define account_option_set(account, option, value) \
weechat_config_option_set(account->options[option], value, 1)
#define account_jid(account) \
account->connection && xmpp_conn_is_connected(account->connection) ? \
xmpp_jid_bare(account->context, xmpp_conn_get_bound_jid(account->connection)) : \
weechat_config_string(account->options[ACCOUNT_OPTION_JID])
#define account_jid_device(account) \
account->connection && xmpp_conn_is_connected(account->connection) ? \
xmpp_conn_get_bound_jid(account->connection) : \
xmpp_jid_new(account->context, \
xmpp_jid_node(account->context, \
weechat_config_string(account->options[ACCOUNT_OPTION_JID])), \
xmpp_jid_domain(account->context, \
weechat_config_string(account->options[ACCOUNT_OPTION_JID])), \
"weechat")
#define account_password(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_PASSWORD])
#define account_tls(account) \
weechat_config_integer(account->options[ACCOUNT_OPTION_TLS])
#define account_nickname(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_NICKNAME])
#define account_autoconnect(account) \
weechat_config_boolean(account->options[ACCOUNT_OPTION_AUTOCONNECT])
#define account_resource(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_RESOURCE])
#define account_status(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_STATUS])
#define account_pgp_pubring_path(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_PGP_PUBRING_PATH])
#define account_pgp_secring_path(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_PGP_SECRING_PATH])
#define account_pgp_keyid(account) \
weechat_config_string(account->options[ACCOUNT_OPTION_PGP_KEYID])
struct t_account_device
{
int id;
char *name;
struct t_account_device *prev_device;
struct t_account_device *next_device;
};
struct t_account_mam_query
{
char *id;
char *with;
int has_start;
time_t start;
int has_end;
time_t end;
struct t_account_mam_query *prev_mam_query;
struct t_account_mam_query *next_mam_query;
};
struct t_account
{
char *name;
struct t_config_option *options[ACCOUNT_NUM_OPTIONS];
int reloading_from_config;
int is_connected;
int disconnected;
int current_retry;
int reconnect_delay;
int reconnect_start;
xmpp_log_t logger;
xmpp_ctx_t *context;
xmpp_conn_t *connection;
struct t_gui_buffer *buffer;
char *buffer_as_string;
struct t_omemo *omemo;
struct t_pgp *pgp;
struct t_account_device *devices;
struct t_account_device *last_device;
struct t_account_mam_query *mam_queries;
struct t_account_mam_query *last_mam_query;
struct t_user *users;
struct t_user *last_user;
struct t_channel *channels;
struct t_channel *last_channel;
struct t_account *prev_account;
struct t_account *next_account;
};
extern char *account_options[][2];
struct t_account *account__search(const char *account_name);
struct t_account *account__casesearch (const char *account_name);
int account__search_option(const char *option_name);
struct t_account_device *account__search_device(struct t_account *account, int id);
void account__add_device(struct t_account *account, struct t_account_device *device);
void account__free_device(struct t_account *account, struct t_account_device *device);
void account__free_device_all(struct t_account *account);
struct t_account_mam_query *account__add_mam_query(struct t_account *account,
struct t_channel *channel,
const char *id,
time_t *start, time_t *end);
struct t_account_mam_query *account__mam_query_search(struct t_account *account,
const char *id);
void account__mam_query_free(struct t_account *account,
struct t_account_mam_query *mam_query);
void account__mam_query_free_all(struct t_account *account);
struct t_account *account__alloc(const char *name);
void account__free_data(struct t_account *account);
void account__free(struct t_account *account);
void account__free_all();
void account__disconnect(struct t_account *account, int reconnect);
void account__disconnect_all();
void account__close_connection(struct t_account *account);
int account__connect(struct t_account *account);
int account__timer_cb(const void *pointer, void *data, int remaining_calls);
#endif /*ACCOUNT_H*/

@ -0,0 +1,191 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <string.h>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "account.h"
#include "channel.h"
#include "buffer.h"
void buffer__get_account_and_channel(struct t_gui_buffer *buffer,
struct t_account **account,
struct t_channel **channel)
{
struct t_account *ptr_account;
struct t_channel *ptr_channel;
if (!buffer)
return;
*account = NULL;
*channel = NULL;
/* look for a account or channel using this buffer */
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
if (ptr_account->buffer == buffer)
{
if (account)
*account = ptr_account;
return;
}
for (ptr_channel = ptr_account->channels; ptr_channel;
ptr_channel = ptr_channel->next_channel)
{
if (ptr_channel->buffer == buffer)
{
if (account)
*account = ptr_account;
if (channel)
*channel = ptr_channel;
return;
}
}
}
}
char *buffer__typing_bar_cb(const void *pointer, void *data,
struct t_gui_bar_item *item,
struct t_gui_window *window,
struct t_gui_buffer *buffer,
struct t_hashtable *extra_info)
{
struct t_channel_typing *ptr_typing;
struct t_account *account;
struct t_channel *channel;
char notification[256];
unsigned typecount;
(void) pointer;
(void) data;
(void) item;
(void) window;
(void) extra_info;
account = NULL;
channel = NULL;
buffer__get_account_and_channel(buffer, &account, &channel);
if (!channel)
return strndup("", 0);
typecount = 0;
for (ptr_typing = channel->typings; ptr_typing;
ptr_typing = ptr_typing->next_typing)
{
switch (++typecount)
{
case 1:
strcpy(notification, ptr_typing->name);
break;
case 2:
strcat(notification, ", ");
strcat(notification, ptr_typing->name);
break;
case 3:
default:
strcpy(notification, "Several people");
break;
}
}
if (typecount)
{
strcat(notification, NG_(" is typing...",
" are typing...",
typecount));
return strdup(notification);
}
else
{
return strndup("", 0);
}
}
int buffer__nickcmp_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *nick1,
const char *nick2)
{
struct t_account *account;
(void) data;
if (pointer)
account = (struct t_account *)pointer;
else
buffer__get_account_and_channel(buffer, &account, NULL);
if (account)
{
return weechat_strcasecmp(nick1, nick2);
}
else
{
return weechat_strcasecmp(nick1, nick2);
}
}
int buffer__close_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer)
{
struct t_weechat_plugin *buffer_plugin = NULL;
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
(void) pointer;
(void) data;
buffer_plugin = weechat_buffer_get_pointer(buffer, "plugin");
if (buffer_plugin != weechat_plugin)
return WEECHAT_RC_OK;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
const char* type = weechat_buffer_get_string(buffer, "localvar_type");
if (weechat_strcasecmp(type, "server") == 0)
{
if (ptr_account)
{
if (ptr_account->is_connected)
{
account__disconnect(ptr_account, 0);
}
ptr_account->buffer = NULL;
}
}
else if (weechat_strcasecmp(type, "channel") == 0)
{
if (ptr_account && ptr_channel)
{
if (ptr_account->is_connected)
{
channel__free(ptr_account, ptr_channel);
}
}
}
else if (weechat_strcasecmp(type, "private") == 0)
{
if (ptr_account && ptr_channel)
{
if (ptr_account->is_connected)
{
channel__free(ptr_account, ptr_channel);
}
}
}
else
{
}
return WEECHAT_RC_OK;
}

@ -0,0 +1,26 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_BUFFER_H_
#define _WEECHAT_XMPP_BUFFER_H_
void buffer__get_account_and_channel(struct t_gui_buffer *buffer,
struct t_account **account,
struct t_channel **channel);
char *buffer__typing_bar_cb(const void *pointer, void *data,
struct t_gui_bar_item *item,
struct t_gui_window *window,
struct t_gui_buffer *buffer,
struct t_hashtable *extra_info);
int buffer__nickcmp_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *nick1,
const char *nick2);
int buffer__close_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer);
#endif /*WEECHAT_XMPP_BUFFER_H*/

File diff suppressed because it is too large Load Diff

@ -0,0 +1,196 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_CHANNEL_H_
#define _WEECHAT_XMPP_CHANNEL_H_
#define CHANNEL_MEMBERS_SPEAKING_LIMIT 128
enum t_channel_type
{
CHANNEL_TYPE_MUC,
CHANNEL_TYPE_PM,
};
enum t_channel_transport
{
CHANNEL_TRANSPORT_PLAINTEXT,
CHANNEL_TRANSPORT_OMEMO,
CHANNEL_TRANSPORT_PGP,
};
struct t_channel_typing
{
union {
char *id;
struct t_user *user;
};
char *name;
time_t ts;
struct t_channel_typing *prev_typing;
struct t_channel_typing *next_typing;
};
struct t_channel_member
{
char *id;
char *role;
char *affiliation;
struct t_channel_member *prev_member;
struct t_channel_member *next_member;
};
struct t_channel_topic
{
char *value;
char *creator;
time_t last_set;
};
struct t_channel_unread
{
char *id;
char *thread;
};
struct t_channel
{
enum t_channel_type type;
char *id;
char *name;
enum t_channel_transport transport;
char *pgp_id;
struct t_channel_topic topic;
/* mpim */
char *creator;
double last_read;
int unread_count;
int unread_count_display;
struct t_hook *typing_hook_timer;
struct t_hook *self_typing_hook_timer;
struct t_weelist *members_speaking[2];
struct t_weelist *unreads;
struct t_channel_typing *self_typings;
struct t_channel_typing *last_self_typing;
struct t_channel_typing *typings;
struct t_channel_typing *last_typing;
struct t_channel_member *members;
struct t_channel_member *last_member;
struct t_gui_buffer *buffer;
char *buffer_as_string;
struct t_channel *prev_channel;
struct t_channel *next_channel;
};
const char *channel__transport_name(enum t_channel_transport transport);
struct t_account *channel__account(struct t_channel *channel);
struct t_channel *channel__search(struct t_account *account,
const char *id);
void channel__add_nicklist_groups(struct t_account *account,
struct t_channel *channel);
struct t_channel *channel__new(struct t_account *account,
enum t_channel_type type,
const char *id, const char *name);
void channel__member_speaking_add(struct t_channel *channel,
const char *nick, int highlight);
void channel__member_speaking_rename(struct t_channel *channel,
const char *old_nick,
const char *new_nick);
void channel__member_speaking_rename_if_present(struct t_account *account,
struct t_channel *channel,
const char *nick);
void channel__typing_free(struct t_channel *channel,
struct t_channel_typing *typing);
void channel__typing_free_all(struct t_channel *channel);
int channel__typing_cb(const void *pointer,
void *data,
int remaining_calls);
struct t_channel_typing *channel__typing_search(struct t_channel *channel,
const char *id);
int channel__add_typing(struct t_channel *channel,
struct t_user *user);
void channel__self_typing_free(struct t_channel *channel,
struct t_channel_typing *typing);
void channel__self_typing_free_all(struct t_channel *channel);
int channel__self_typing_cb(const void *pointer,
void *data,
int remaining_calls);
struct t_channel_typing *channel__self_typing_search(struct t_channel *channel,
struct t_user *user);
int channel__add_self_typing(struct t_channel *channel,
struct t_user *user);
int channel__hotlist_update_cb(const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data);
void channel__unread_free(struct t_channel_unread *unread);
void channel__unread_free_all(struct t_channel *channel);
void channel__free(struct t_account *account,
struct t_channel *channel);
void channel__free_all(struct t_account *account);
void channel__update_topic(struct t_channel *channel,
const char* title,
const char* creator,
int last_set);
void channel__update_purpose(struct t_channel *channel,
const char* purpose,
const char* creator,
int last_set);
struct t_channel_member *channel__add_member(struct t_account *account,
struct t_channel *channel,
const char *id, const char *client);
struct t_channel_member *channel__member_search(struct t_channel *channel,
const char *id);
struct t_channel_member *channel__remove_member(struct t_account *account,
struct t_channel *channel,
const char *id, const char *reason);
void channel__send_message(struct t_account *account, struct t_channel *channel,
const char *to, const char *body);
void channel__send_reads(struct t_account *account, struct t_channel *channel);
void channel__send_typing(struct t_account *account, struct t_channel *channel,
struct t_user *user);
void channel__send_paused(struct t_account *account, struct t_channel *channel,
struct t_user *user);
void channel__fetch_mam(struct t_account *account, struct t_channel *channel,
const char *id, time_t *start, time_t *end, const char *after);
#endif /*WEECHAT_XMPP_CHANNEL_H*/

@ -0,0 +1,927 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <strophe.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
//#include "oauth.h"
#include "account.h"
#include "user.h"
#include "channel.h"
#include "buffer.h"
#include "message.h"
#include "command.h"
#define MAM_DEFAULT_DAYS 2
#define STR(X) #X
void command__display_account(struct t_account *account)
{
int num_channels, num_pv;
if (account->is_connected)
{
num_channels = 0;//xmpp_account_get_channel_count(account);
num_pv = 0;//xmpp_account_get_pv_count(account);
weechat_printf(
NULL,
" %s %s%s%s %s(%s%s%s) [%s%s%s]%s, %d %s, %d pv",
(account->is_connected) ? "*" : " ",
weechat_color("chat_server"),
account->name,
weechat_color("reset"),
weechat_color("chat_delimiters"),
weechat_color("chat_server"),
account_jid(account),
weechat_color("chat_delimiters"),
weechat_color("reset"),
(account->is_connected) ? _("connected") : _("not connected"),
weechat_color("chat_delimiters"),
weechat_color("reset"),
num_channels,
NG_("channel", "channels", num_channels),
num_pv);
}
else
{
weechat_printf(
NULL,
" %s%s%s %s(%s%s%s)%s",
weechat_color("chat_server"),
account->name,
weechat_color("reset"),
weechat_color("chat_delimiters"),
weechat_color("chat_server"),
account_jid(account),
weechat_color("chat_delimiters"),
weechat_color("reset"));
}
}
void command__account_list(int argc, char **argv)
{
int i, one_account_found;
struct t_account *ptr_account2;
char *account_name = NULL;
for (i = 2; i < argc; i++)
{
if (!account_name)
account_name = argv[i];
}
if (!account_name)
{
if (accounts)
{
weechat_printf(NULL, "");
weechat_printf(NULL, _("All accounts:"));
for (ptr_account2 = accounts; ptr_account2;
ptr_account2 = ptr_account2->next_account)
{
command__display_account(ptr_account2);
}
}
else
weechat_printf(NULL, _("No account"));
}
else
{
one_account_found = 0;
for (ptr_account2 = accounts; ptr_account2;
ptr_account2 = ptr_account2->next_account)
{
if (weechat_strcasestr(ptr_account2->name, account_name))
{
if (!one_account_found)
{
weechat_printf(NULL, "");
weechat_printf(NULL,
_("Servers with \"%s\":"),
account_name);
}
one_account_found = 1;
command__display_account(ptr_account2);
}
}
if (!one_account_found)
weechat_printf(NULL,
_("No account found with \"%s\""),
account_name);
}
}
void command__add_account(const char *name, const char *jid, const char *password)
{
struct t_account *account;
account = account__casesearch(name);
if (account)
{
weechat_printf(
NULL,
_("%s%s: account \"%s\" already exists, can't add it!"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
name);
return;
}
account = account__alloc(name);
if (!account)
{
weechat_printf(
NULL,
_("%s%s: unable to add account"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return;
}
account->name = strdup(name);
if (jid)
account_option_set(account, ACCOUNT_OPTION_JID, strdup(jid));
if (password)
account_option_set(account, ACCOUNT_OPTION_PASSWORD, strdup(password));
if (jid)
account_option_set(account, ACCOUNT_OPTION_NICKNAME,
strdup(xmpp_jid_node(account->context, jid)));
weechat_printf(
NULL,
_("%s: account %s%s%s %s(%s%s%s)%s added"),
WEECHAT_XMPP_PLUGIN_NAME,
weechat_color("chat_server"),
account->name,
weechat_color("reset"),
weechat_color("chat_delimiters"),
weechat_color("chat_server"),
jid ? jid : "???",
weechat_color("chat_delimiters"),
weechat_color("reset"));
}
void command__account_add(struct t_gui_buffer *buffer, int argc, char **argv)
{
char *name, *jid = NULL, *password = NULL;
(void) buffer;
switch (argc)
{
case 5:
password = argv[4];
// fall through
case 4:
jid = argv[3];
// fall through
case 3:
name = argv[2];
command__add_account(name, jid, password);
break;
default:
weechat_printf(NULL, _("account add: wrong number of arguments"));
break;
}
}
int command__connect_account(struct t_account *account)
{
if (!account)
return 0;
if (account->is_connected)
{
weechat_printf(
NULL,
_("%s%s: already connected to account \"%s\"!"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
account->name);
}
account__connect(account);
return 1;
}
int command__account_connect(struct t_gui_buffer *buffer, int argc, char **argv)
{
int i, nb_connect, connect_ok;
struct t_account *ptr_account;
(void) buffer;
(void) argc;
(void) argv;
connect_ok = 1;
nb_connect = 0;
for (i = 2; i < argc; i++)
{
nb_connect++;
ptr_account = account__search(argv[i]);
if (ptr_account)
{
if (!command__connect_account(ptr_account))
{
connect_ok = 0;
}
}
else
{
weechat_printf(
NULL,
_("%s%s: account not found \"%s\" "
"(add one first with: /account add)"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
argv[i]);
}
}
return (connect_ok) ? WEECHAT_RC_OK : WEECHAT_RC_ERROR;
}
int command__disconnect_account(struct t_account *account)
{
if (!account)
return 0;
if (!account->is_connected)
{
weechat_printf(
NULL,
_("%s%s: not connected to account \"%s\"!"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
account->name);
}
account__disconnect(account, 0);
return 1;
}
int command__account_disconnect(struct t_gui_buffer *buffer, int argc, char **argv)
{
int i, nb_disconnect, disconnect_ok;
struct t_account *ptr_account;
(void) argc;
(void) argv;
disconnect_ok = 1;
nb_disconnect = 0;
if (argc < 2)
{
struct t_channel *ptr_channel;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (ptr_account)
{
if (!command__disconnect_account(ptr_account))
{
disconnect_ok = 0;
}
}
}
for (i = 2; i < argc; i++)
{
nb_disconnect++;
ptr_account = account__search(argv[i]);
if (ptr_account)
{
if (!command__disconnect_account(ptr_account))
{
disconnect_ok = 0;
}
}
else
{
weechat_printf(
NULL,
_("%s%s: account not found \"%s\" "),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
argv[i]);
}
}
return (disconnect_ok) ? WEECHAT_RC_OK : WEECHAT_RC_ERROR;
}
int command__account_reconnect(struct t_gui_buffer *buffer, int argc, char **argv)
{
command__account_disconnect(buffer, argc, argv);
return command__account_connect(buffer, argc, argv);
}
void command__account_delete(struct t_gui_buffer *buffer, int argc, char **argv)
{
(void) buffer;
struct t_account *account;
char *account_name;
if (argc < 3)
{
weechat_printf(
NULL,
_("%sToo few arguments for command\"%s %s\" "
"(help on command: /help %s)"),
weechat_prefix("error"),
argv[0], argv[1], argv[0] + 1);
return;
}
account = account__search(argv[2]);
if (!account)
{
weechat_printf(
NULL,
_("%s%s: account \"%s\" not found for \"%s\" command"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
argv[2], "xmpp delete");
return;
}
if (account->is_connected)
{
weechat_printf(
NULL,
_("%s%s: you cannot delete account \"%s\" because you"
"are connected. Try \"/xmpp disconnect %s\" first."),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
argv[2], argv[2]);
return;
}
account_name = strdup(account->name);
account__free(account);
weechat_printf(
NULL,
_("%s: account %s%s%s has been deleted"),
WEECHAT_XMPP_PLUGIN_NAME,
weechat_color("chat_server"),
(account_name) ? account_name : "???",
weechat_color("reset"));
if (account_name)
free(account_name);
}
int command__account(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
(void) pointer;
(void) data;
(void) buffer;
if (argc <= 1 || weechat_strcasecmp(argv[1], "list") == 0)
{
command__account_list(argc, argv);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
if (weechat_strcasecmp(argv[1], "add") == 0)
{
command__account_add(buffer, argc, argv);
return WEECHAT_RC_OK;
}
if (weechat_strcasecmp(argv[1], "connect") == 0)
{
command__account_connect(buffer, argc, argv);
return WEECHAT_RC_OK;
}
if (weechat_strcasecmp(argv[1], "disconnect") == 0)
{
command__account_disconnect(buffer, argc, argv);
return WEECHAT_RC_OK;
}
if (weechat_strcasecmp(argv[1], "reconnect") == 0)
{
command__account_reconnect(buffer, argc, argv);
return WEECHAT_RC_OK;
}
if (weechat_strcasecmp(argv[1], "delete") == 0)
{
command__account_delete(buffer, argc, argv);
return WEECHAT_RC_OK;
}
WEECHAT_COMMAND_ERROR;
}
return WEECHAT_RC_OK;
}
int command__enter(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
xmpp_stanza_t *pres;
char *jid, *pres_jid, *text;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
int n_jid = 0;
char **jids = weechat_string_split(argv[1], ",", NULL, 0, 0, &n_jid);
for (int i = 0; i < n_jid; i++)
{
jid = xmpp_jid_bare(ptr_account->context, jids[i]);
pres_jid = jids[i];
if(!xmpp_jid_resource(ptr_account->context, pres_jid))
pres_jid = xmpp_jid_new(
ptr_account->context,
xmpp_jid_node(ptr_account->context, jid),
xmpp_jid_domain(ptr_account->context, jid),
account_nickname(ptr_account)
&& strlen(account_nickname(ptr_account))
? account_nickname(ptr_account)
: xmpp_jid_node(ptr_account->context,
account_jid(ptr_account)));
ptr_channel = channel__search(ptr_account, jid);
if (!ptr_channel)
ptr_channel = channel__new(ptr_account, CHANNEL_TYPE_MUC, jid, jid);
pres = xmpp_presence_new(ptr_account->context);
xmpp_stanza_set_to(pres, pres_jid);
xmpp_stanza_set_from(pres, account_jid(ptr_account));
xmpp_stanza_t *pres__x = xmpp_stanza_new(ptr_account->context);
xmpp_stanza_set_name(pres__x, "x");
xmpp_stanza_set_ns(pres__x, "http://jabber.org/protocol/muc");
xmpp_stanza_add_child(pres, pres__x);
xmpp_stanza_release(pres__x);
xmpp_send(ptr_account->connection, pres);
xmpp_stanza_release(pres);
if (argc > 2)
{
text = argv_eol[2];
channel__send_message(ptr_account, ptr_channel, jid, text);
}
char buf[16];
int num = weechat_buffer_get_integer(ptr_channel->buffer, "number");
snprintf(buf, sizeof(buf), "/buffer %d", num);
weechat_command(ptr_account->buffer, buf);
}
weechat_string_free_split(jids);
}
else
{
const char *buffer_jid = weechat_buffer_get_string(buffer, "localvar_channel");
pres_jid = xmpp_jid_new(
ptr_account->context,
xmpp_jid_node(ptr_account->context, buffer_jid),
xmpp_jid_domain(ptr_account->context, buffer_jid),
weechat_buffer_get_string(buffer, "localvar_nick"));
ptr_channel = channel__search(ptr_account, buffer_jid);
if (!ptr_channel)
ptr_channel = channel__new(ptr_account, CHANNEL_TYPE_MUC, buffer_jid, buffer_jid);
pres = xmpp_presence_new(ptr_account->context);
xmpp_stanza_set_to(pres, pres_jid);
xmpp_stanza_set_from(pres, account_jid(ptr_account));
xmpp_stanza_t *pres__x = xmpp_stanza_new(ptr_account->context);
xmpp_stanza_set_name(pres__x, "x");
xmpp_stanza_set_ns(pres__x, "http://jabber.org/protocol/muc");
xmpp_stanza_add_child(pres, pres__x);
xmpp_stanza_release(pres__x);
xmpp_send(ptr_account->connection, pres);
xmpp_stanza_release(pres);
}
return WEECHAT_RC_OK;
}
int command__open(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
xmpp_stanza_t *pres;
char *jid, *text;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
int n_jid = 0;
char **jids = weechat_string_split(argv[1], ",", NULL, 0, 0, &n_jid);
for (int i = 0; i < n_jid; i++)
{
jid = xmpp_jid_bare(ptr_account->context, jids[i]);
pres = xmpp_presence_new(ptr_account->context);
xmpp_stanza_set_to(pres, jid);
xmpp_stanza_set_from(pres, account_jid(ptr_account));
xmpp_send(ptr_account->connection, pres);
xmpp_stanza_release(pres);
ptr_channel = channel__search(ptr_account, jid);
if (!ptr_channel)
ptr_channel = channel__new(ptr_account, CHANNEL_TYPE_PM, jid, jid);
if (argc > 2)
{
text = argv_eol[2];
channel__send_message(ptr_account, ptr_channel, jid, text);
}
char buf[16];
int num = weechat_buffer_get_integer(ptr_channel->buffer, "number");
snprintf(buf, sizeof(buf), "/buffer %d", num);
weechat_command(ptr_account->buffer, buf);
}
weechat_string_free_split(jids);
}
return WEECHAT_RC_OK;
}
int command__msg(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
xmpp_stanza_t *message;
char *text;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_channel)
{
weechat_printf(
ptr_account->buffer,
_("%s%s: \"%s\" command can not be executed on a account buffer"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "msg");
return WEECHAT_RC_OK;
}
if (!ptr_account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
text = argv_eol[1];
message = xmpp_message_new(ptr_account->context,
ptr_channel->type == CHANNEL_TYPE_MUC ? "groupchat" : "chat",
ptr_channel->name, NULL);
xmpp_message_set_body(message, text);
xmpp_send(ptr_account->connection, message);
xmpp_stanza_release(message);
if (ptr_channel->type != CHANNEL_TYPE_MUC)
weechat_printf_date_tags(ptr_channel->buffer, 0,
"xmpp_message,message,private,notify_none,self_msg,log1",
"%s\t%s",
user__as_prefix_raw(ptr_account, account_jid(ptr_account)), text);
}
return WEECHAT_RC_OK;
}
int command__me(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
xmpp_stanza_t *message;
char *text;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_channel)
{
weechat_printf(
ptr_account->buffer,
_("%s%s: \"%s\" command can not be executed on a account buffer"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "me");
return WEECHAT_RC_OK;
}
if (!ptr_account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
text = argv_eol[0];
message = xmpp_message_new(ptr_account->context,
ptr_channel->type == CHANNEL_TYPE_MUC ? "groupchat" : "chat",
ptr_channel->name, NULL);
xmpp_message_set_body(message, text);
xmpp_send(ptr_account->connection, message);
xmpp_stanza_release(message);
if (ptr_channel->type != CHANNEL_TYPE_MUC)
weechat_printf_date_tags(ptr_channel->buffer, 0,
"xmpp_message,message,action,private,notify_none,self_msg,log1",
"%s%s %s",
weechat_prefix("action"),
user__as_prefix_raw(ptr_account, account_jid(ptr_account)),
strlen(text) > strlen("/me ") ? text+4 : "");
}
return WEECHAT_RC_OK;
}
int command__mam(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
int days;
(void) pointer;
(void) data;
(void) argv_eol;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_channel)
{
weechat_printf(
ptr_account->buffer,
_("%s%s: \"%s\" command can not be executed on a account buffer"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "mam");
return WEECHAT_RC_OK;
}
time_t start = time(NULL);
struct tm *ago = gmtime(&start);
if (argc > 1)
{
errno = 0;
days = strtol(argv[1], NULL, 10);
if (errno == 0)
ago->tm_mday -= days;
else
{
weechat_printf(
ptr_channel->buffer,
_("%s%s: \"%s\" is not a valid number of %s for %s"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "days", "mam");
ago->tm_mday -= MAM_DEFAULT_DAYS;
}
}
else
ago->tm_mday -= MAM_DEFAULT_DAYS;
start = mktime(ago);
channel__fetch_mam(ptr_account, ptr_channel, NULL, &start, NULL, NULL);
return WEECHAT_RC_OK;
}
int command__pgp(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
char *keyid;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account)
return WEECHAT_RC_ERROR;
if (!ptr_channel)
{
weechat_printf(
ptr_account->buffer,
_("%s%s: \"%s\" command can not be executed on a account buffer"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "pgp");
return WEECHAT_RC_OK;
}
if (argc > 1)
{
keyid = argv_eol[1];
ptr_channel->pgp_id = strdup(keyid);
}
else
{
ptr_channel->pgp_id = NULL;
}
return WEECHAT_RC_OK;
}
int command__xml(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol)
{
struct t_account *ptr_account = NULL;
struct t_channel *ptr_channel = NULL;
xmpp_stanza_t *stanza;
(void) pointer;
(void) data;
(void) argv;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (!ptr_account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
if (argc > 1)
{
stanza = xmpp_stanza_new_from_string(ptr_account->context,
argv_eol[0]);
if (!stanza)
return WEECHAT_RC_ERROR;
xmpp_send(ptr_account->connection, stanza);
xmpp_stanza_release(stanza);
}
return WEECHAT_RC_OK;
}
void command__init()
{
struct t_hook *hook;
hook = weechat_hook_command(
"account",
N_("handle xmpp accounts"),
N_("list"
" || add <account>"
" || connect <account>"
" || disconnect <account>"
" || reconnect <account>"
" || delete <account>"),
N_(" list: list accounts\n"
" add: add a xmpp account\n"
" connect: connect to a xmpp account\n"
"disconnect: disconnect from a xmpp account\n"
" reconnect: reconnect an xmpp account\n"
" delete: delete a xmpp account\n"),
"list"
" || add %(xmpp_account)"
" || connect %(xmpp_account)"
" || disconnect %(xmpp_account)"
" || reconnect %(xmpp_account)"
" || delete %(xmpp_account)",
&command__account, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /account");
hook = weechat_hook_command(
"enter",
N_("enter an xmpp multi-user-chat (muc)"),
N_("<jid>"),
N_("jid: muc to enter"),
NULL, &command__enter, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /enter");
hook = weechat_hook_command(
"open",
N_("open a direct xmpp chat"),
N_("<jid>"),
N_("jid: jid to target"),
NULL, &command__open, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /open");
hook = weechat_hook_command(
"msg",
N_("send a xmpp message to the current buffer"),
N_("<message>"),
N_("message: message to send"),
NULL, &command__msg, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /msg");
hook = weechat_hook_command(
"me",
N_("send a xmpp action to the current buffer"),
N_("<message>"),
N_("message: message to send"),
NULL, &command__me, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /me");
hook = weechat_hook_command(
"mam",
N_("retrieve mam messages for the current channel"),
N_("[days]"),
N_("days: number of days to fetch (default: " STR(MAM_DEFAULT_DAYS) ")"),
NULL, &command__mam, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /mam");
hook = weechat_hook_command(
"pgp",
N_("set the target pgp key for the current channel"),
N_("<keyid>"),
N_("keyid: recipient keyid"),
NULL, &command__pgp, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /pgp");
hook = weechat_hook_command(
"xml",
N_("send a raw xml stanza"),
N_("<stanza>"),
N_("stanza: xml to send"),
NULL, &command__xml, NULL, NULL);
if (!hook)
weechat_printf(NULL, "Failed to setup command /xml");
}

@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_COMMAND_H_
#define _WEECHAT_XMPP_COMMAND_H_
int command__enter(const void *pointer, void *data,
struct t_gui_buffer *buffer, int argc,
char **argv, char **argv_eol);
void command__init();
#endif /*WEECHAT_XMPP_COMMAND_H*/

@ -0,0 +1,155 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "config.h"
#include "account.h"
#include "channel.h"
#include "user.h"
#include "buffer.h"
#include "completion.h"
void completion__channel_nicks_add_speakers(struct t_gui_completion *completion,
struct t_account *account,
struct t_channel *channel,
int highlight)
{
struct t_user *user;
const char *member;
int list_size, i;
if (channel->members_speaking[highlight])
{
list_size = weechat_list_size(channel->members_speaking[highlight]);
for (i = 0; i < list_size; i++)
{
member = weechat_list_string(
weechat_list_get(channel->members_speaking[highlight], i));
if (member)
{
user = user__search(account, member);
if (user)
weechat_hook_completion_list_add(completion,
user->profile.display_name,
1, WEECHAT_LIST_POS_BEGINNING);
}
}
}
}
int completion__channel_nicks_cb(const void *pointer, void *data,
const char *completion_item,
struct t_gui_buffer *buffer,
struct t_gui_completion *completion)
{
struct t_account *ptr_account;
struct t_channel *ptr_channel;
struct t_channel_member *ptr_member;
struct t_user *ptr_user;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) completion_item;
ptr_account = NULL;
ptr_channel = NULL;
buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel);
if (ptr_channel)
{
switch (ptr_channel->type)
{
case CHANNEL_TYPE_MUC:
case CHANNEL_TYPE_PM:
for (ptr_member = ptr_channel->members; ptr_member;
ptr_member = ptr_member->next_member)
{
ptr_user = user__search(ptr_account, ptr_member->id);
if (ptr_user)
weechat_hook_completion_list_add(completion,
ptr_user->profile.display_name,
1, WEECHAT_LIST_POS_SORT);
}
/* add recent speakers on channel */
if (weechat_config_integer(config_look_nick_completion_smart) == CONFIG_NICK_COMPLETION_SMART_SPEAKERS)
{
completion__channel_nicks_add_speakers(completion, ptr_account, ptr_channel, 0);
}
/* add members whose make highlights on me recently on this channel */
if (weechat_config_integer(config_look_nick_completion_smart) == CONFIG_NICK_COMPLETION_SMART_SPEAKERS_HIGHLIGHTS)
{
completion__channel_nicks_add_speakers(completion, ptr_account, ptr_channel, 1);
}
/* add self member at the end */
weechat_hook_completion_list_add(completion,
ptr_account->name,
1, WEECHAT_LIST_POS_END);
break;
}
}
return WEECHAT_RC_OK;
}
int completion__accounts_cb(const void *pointer, void *data,
const char *completion_item,
struct t_gui_buffer *buffer,
struct t_gui_completion *completion)
{
struct t_account *ptr_account;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) completion_item;
(void) buffer;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
weechat_hook_completion_list_add(completion, account_jid(ptr_account),
0, WEECHAT_LIST_POS_SORT);
}
return WEECHAT_RC_OK;
}
void completion__init()
{
struct t_config_option *option;
const char *default_template;
weechat_hook_completion("nick",
N_("nicks of current Slack channel"),
&completion__channel_nicks_cb,
NULL, NULL);
weechat_hook_completion("account",
N_("xmpp accounts"),
&completion__accounts_cb,
NULL, NULL);
option = weechat_config_get("weechat.completion.default_template");
default_template = weechat_config_string(option);
if (!weechat_strcasestr(default_template, "%(account)"))
{
size_t length = snprintf(NULL, 0, "%s|%s",
default_template,
"%(account)") + 1;
char *new_template = malloc(length);
snprintf(new_template, length, "%s|%s",
default_template,
"%(account)");
weechat_config_option_set(option, new_template, 1);
free(new_template);
}
}

@ -0,0 +1,10 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_COMPLETION_H_
#define _WEECHAT_XMPP_COMPLETION_H_
void completion__init();
#endif /*WEECHAT_XMPP_COMPLETION_H*/

@ -0,0 +1,498 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <string.h>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "account.h"
#include "config.h"
struct t_config_file *config_file;
struct t_config_section *config_section_account_default;
struct t_config_section *config_section_account;
struct t_config_option *config_look_nick_completion_smart;
struct t_config_option *config_account_default[ACCOUNT_NUM_OPTIONS];
int config__account_check_value_cb(const void *pointer, void *data,
struct t_config_option *option,
const char *value)
{
(void) pointer;
(void) data;
(void) option;
(void) value;
return 1;
}
void config__account_change_cb(const void *pointer, void *data,
struct t_config_option *option)
{
(void) pointer;
(void) data;
const char *name =
weechat_config_option_get_string(option, "name");
const char *value =
weechat_config_option_get_string(option, "value");
int split_num;
char **split = weechat_string_split(name, ".", NULL, 0, 2, &split_num);
struct t_account *account = account__search(split[0]);
if (split_num >= 2 && account)
{
const char *key = split[1];
(void) key;
(void) value;
}
weechat_string_free_split(split);
}
void config__account_default_change_cb(const void *pointer, void *data,
struct t_config_option *option)
{
(void) pointer;
(void) data;
(void) option;
}
struct t_config_option *
config__account_new_option (struct t_config_file *config_file,
struct t_config_section *section,
int index_option,
const char *option_name,
const char *default_value,
const char *value,
int null_value_allowed,
int (*callback_check_value)(const void *pointer,
void *data,
struct t_config_option *option,
const char *value),
const void *callback_check_value_pointer,
void *callback_check_value_data,
void (*callback_change)(const void *pointer,
void *data,
struct t_config_option *option),
const void *callback_change_pointer,
void *callback_change_data)
{
struct t_config_option *new_option;
new_option = NULL;
switch (index_option)
{
case ACCOUNT_OPTION_JID:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account JID"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_PASSWORD:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account Password"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_TLS:
new_option = weechat_config_new_option (
config_file, section,
option_name, "integer",
N_("XMPP Server TLS Policy"),
"disable|normal|trust", 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_NICKNAME:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account Nickname"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_AUTOCONNECT:
new_option = weechat_config_new_option (
config_file, section,
option_name, "boolean",
N_("Autoconnect XMPP Account"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_RESOURCE:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account Resource"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_STATUS:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account Login Status"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_PGP_PUBRING_PATH:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account PGP Public Keyring Path"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_PGP_SECRING_PATH:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account PGP Secure Keyring Path"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_OPTION_PGP_KEYID:
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
N_("XMPP Account PGP Key ID"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
callback_check_value,
callback_check_value_pointer,
callback_check_value_data,
callback_change,
callback_change_pointer,
callback_change_data,
NULL, NULL, NULL);
break;
case ACCOUNT_NUM_OPTIONS:
break;
}
return new_option;
}
void config__account_create_default_options(struct t_config_section *section)
{
int i;
for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
{
config_account_default[i] = config__account_new_option(
config_file,
section,
i,
account_options[i][0],
account_options[i][1],
account_options[i][1],
0,
&config__account_check_value_cb,
account_options[i][0],
NULL,
&config__account_default_change_cb,
account_options[i][0],
NULL);
}
}
int config__account_read_cb (const void *pointer, void *data,
struct t_config_file *config_file,
struct t_config_section *section,
const char *option_name, const char *value)
{
struct t_account *ptr_account;
int index_option, rc, i;
char *pos_option, *account_name;
(void) pointer;
(void) data;
(void) config_file;
(void) section;
rc = WEECHAT_CONFIG_OPTION_SET_ERROR;
if (option_name)
{
pos_option = strrchr(option_name, '.');
if (pos_option)
{
account_name = weechat_strndup(option_name,
pos_option - option_name);
pos_option++;
if (account_name)
{
index_option = account__search_option(pos_option);
if (index_option >= 0)
{
ptr_account = account__search(account_name);
if (!ptr_account)
ptr_account = account__alloc(account_name);
if (ptr_account)
{
if (!ptr_account->reloading_from_config++)
{
for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
{
weechat_config_option_set(
ptr_account->options[i], NULL, 1);
}
}
ptr_account->reloading_from_config %=
ACCOUNT_NUM_OPTIONS;
rc = weechat_config_option_set(
ptr_account->options[index_option], value, 1);
if (!ptr_account->reloading_from_config)
{
const char *ac_global = weechat_info_get("auto_connect", NULL);
int ac_local = weechat_config_boolean(
ptr_account->options[ACCOUNT_OPTION_AUTOCONNECT]);
if (ac_local && (strcmp(ac_global, "1") == 0))
account__connect(ptr_account);
}
}
else
{
weechat_printf(
NULL,
_("%s%s: error adding account \"%s\""),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
account_name);
}
}
free(account_name);
}
}
}
if (rc == WEECHAT_CONFIG_OPTION_SET_ERROR)
{
weechat_printf(
NULL,
_("%s%s: error creating account option \"%s\""),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, option_name);
}
return rc;
}
int config__account_write_cb(const void *pointer, void *data,
struct t_config_file *config_file,
const char *section_name)
{
struct t_account *ptr_account;
int i;
(void) pointer;
(void) data;
if (!weechat_config_write_line(config_file, section_name, NULL))
return WEECHAT_CONFIG_WRITE_ERROR;
for (ptr_account = accounts; ptr_account;
ptr_account = ptr_account->next_account)
{
for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++)
{
if (!weechat_config_write_option(config_file,
ptr_account->options[i]))
return WEECHAT_CONFIG_WRITE_ERROR;
}
}
return WEECHAT_CONFIG_WRITE_OK;
}
int config__reload (const void *pointer, void *data,
struct t_config_file *config_file)
{
(void) pointer;
(void) data;
weechat_config_section_free_options(config_section_account_default);
weechat_config_section_free_options(config_section_account);
account__free_all();
return weechat_config_reload(config_file);
}
int config__init()
{
struct t_config_section *ptr_section;
config_file = weechat_config_new(WEECHAT_XMPP_CONFIG_NAME,
&config__reload, NULL, NULL);
if(!config_file)
return 0;
ptr_section = weechat_config_new_section(
config_file, "look",
0, 0,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL);
if (!ptr_section)
{
weechat_config_free(config_file);
config_file = NULL;
return 0;
}
config_look_nick_completion_smart = weechat_config_new_option (
config_file, ptr_section,
"nick_completion_smart", "integer",
N_("smart completion for nicks (completes first with last speakers): "
"speakers = all speakers (including highlights), "
"speakers_highlights = only speakers with highlight"),
"off|speakers|speakers_highlights", 0, 0, "speakers", NULL, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
ptr_section = weechat_config_new_section(
config_file, "account_default",
0, 0,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL);
if (!ptr_section)
{
weechat_config_free(config_file);
config_file = NULL;
return 0;
}
config_section_account_default = ptr_section;
config__account_create_default_options(ptr_section);
ptr_section = weechat_config_new_section(
config_file, "account",
0, 0,
&config__account_read_cb, NULL, NULL,
&config__account_write_cb, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL);
if (!ptr_section)
{
weechat_config_free(config_file);
config_file = NULL;
return 0;
}
config_section_account = ptr_section;
return 1;
}
int config__read()
{
int rc;
rc = weechat_config_read(config_file);
return rc;
}
int config__write()
{
return weechat_config_write(config_file);
}
void config__free()
{
}

@ -0,0 +1,57 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_CONFIG_H_
#define _WEECHAT_XMPP_CONFIG_H_
#define WEECHAT_XMPP_CONFIG_NAME "xmpp"
enum t_config_nick_completion
{
CONFIG_NICK_COMPLETION_SMART_OFF = 0,
CONFIG_NICK_COMPLETION_SMART_SPEAKERS,
CONFIG_NICK_COMPLETION_SMART_SPEAKERS_HIGHLIGHTS,
};
extern struct t_config_file *config_file;
extern struct t_config_section *config_section_account_default;
extern struct t_config_section *config_section_account;
extern struct t_config_option *config_look_nick_completion_smart;
extern struct t_config_option *config_account_default[];
int config__account_check_value_cb(const void *pointer, void *data,
struct t_config_option *option,
const char *value);
void config__account_change_cb(const void *pointer, void *data,
struct t_config_option *option);
struct t_config_option *config__account_new_option (struct t_config_file *config_file,
struct t_config_section *section,
int index_option,
const char *option_name,
const char *default_value,
const char *value,
int null_value_allowed,
int (*callback_check_value)(const void *pointer,
void *data,
struct t_config_option *option,
const char *value),
const void *callback_check_value_pointer,
void *callback_check_value_data,
void (*callback_change)(const void *pointer,
void *data,
struct t_config_option *option),
const void *callback_change_pointer,
void *callback_change_data);
extern int config__init();
extern int config__read();
extern int config__write();
extern void config__free();
#endif /*WEECHAT_XMPP_CONFIG_H*/

File diff suppressed because it is too large Load Diff

@ -0,0 +1,16 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_CONNECTION_H_
#define _WEECHAT_XMPP_CONNECTION_H_
void connection__init();
int connection__connect(struct t_account *account, xmpp_conn_t **connection,
const char* jid, const char* password, int tls);
void connection__process(xmpp_ctx_t *context, xmpp_conn_t *connection,
const unsigned long timeout);
#endif /*WEECHAT_XMPP_CONNECTION_H*/

@ -0,0 +1,84 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <strophe.h>
#include <stdlib.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "account.h"
#include "channel.h"
#include "buffer.h"
#include "message.h"
#include "input.h"
int input__data(struct t_gui_buffer *buffer, const char *text)
{
struct t_account *account = NULL;
struct t_channel *channel = NULL;
buffer__get_account_and_channel(buffer, &account, &channel);
if (!account)
return WEECHAT_RC_ERROR;
if (channel)
{
if (!account->is_connected)
{
weechat_printf(buffer,
_("%s%s: you are not connected to server"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return WEECHAT_RC_OK;
}
channel__send_message(account, channel, channel->id, text);
}
else
{
weechat_printf(buffer,
_("%s%s: this buffer is not a channel!"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
}
return WEECHAT_RC_OK;
}
int input__data_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *text)
{
(void) pointer;
(void) data;
return input__data(buffer, text);
}
int input__typing(struct t_gui_buffer *buffer)
{
struct t_account *account = NULL;
struct t_channel *channel = NULL;
buffer__get_account_and_channel(buffer, &account, &channel);
if (account && account->is_connected && channel)
{
channel__send_reads(account, channel);
channel__send_typing(account, channel, NULL);
}
return WEECHAT_RC_OK;
}
int input__text_changed_cb(const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data)
{
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
return input__typing(signal_data);
}

@ -0,0 +1,16 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_INPUT_H_
#define _WEECHAT_XMPP_INPUT_H_
int input__data_cb(const void *pointer, void *data,
struct t_gui_buffer *buffer,
const char *input_data);
int input__text_changed_cb(const void *pointer, void *data,
const char *signal, const char *type_data,
void *signal_data);
#endif /*WEECHAT_XMPP_INPUT_H*/

@ -0,0 +1,249 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <strophe.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <regex.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "account.h"
#include "channel.h"
#include "user.h"
#include "message.h"
static const char format_regex[] = "<([^>]*?)>";
static const size_t max_groups = 2;
char *message__translate_code(struct t_account *account,
const char *code)
{
struct t_channel *channel;
struct t_user *user;
size_t resultlen;
char *identifier, *alttext, *result, *symbol, *prefix;
identifier = strdup(code);
alttext = strchr(identifier, '|');
if (alttext)
*alttext++ = '\0';
switch (identifier[0])
{
case '#': /* channel */
if (alttext)
{
prefix = "#";
symbol = strdup(alttext);
}
else
{
channel = channel__search(account, identifier+1);
if (channel)
{
prefix = "#";
symbol = strdup(channel->name);
}
else
{
prefix = "Channel:";
symbol = strdup(identifier+1);
}
}
break;
case '@': /* user */
if (alttext)
{
prefix = "@";
symbol = strdup(alttext);
}
else
{
user = user__search(account, identifier+1);
if (user)
{
prefix = "@";
symbol = strdup(user->profile.display_name);
}
else
{
prefix = "User:";
symbol = strdup(identifier+1);
}
}
break;
case '!': /* special */
if (alttext)
{
prefix = "@";
symbol = strdup(alttext);
}
else
{
prefix = "@";
symbol = strdup(identifier+1);
}
break;
default: /* url */
prefix = "";
symbol = strdup(code);
break;
}
free(identifier);
resultlen = snprintf(NULL, 0, "%s%s%s%s", weechat_color("chat_nick"), prefix, symbol, weechat_color("reset")) + 1;
result = malloc(resultlen);
snprintf(result, resultlen, "%s%s%s%s", weechat_color("chat_nick"), prefix, symbol, weechat_color("reset"));
free(symbol);
return result;
}
void message__htmldecode(char *dest, const char *src, size_t n)
{
size_t i, j;
for (i = 0, j = 0; i < n; i++, j++)
switch (src[i])
{
case '\0':
dest[j] = '\0';
return;
case '&':
if (src[i+1] == 'g' &&
src[i+2] == 't' &&
src[i+3] == ';')
{
dest[j] = '>';
i += 3;
break;
}
else if (src[i+1] == 'l' &&
src[i+2] == 't' &&
src[i+3] == ';')
{
dest[j] = '<';
i += 3;
break;
}
else if (src[i+1] == 'a' &&
src[i+2] == 'm' &&
src[i+3] == 'p' &&
src[i+4] == ';')
{
dest[j] = '&';
i += 4;
break;
}
/* fallthrough */
default:
dest[j] = src[i];
break;
}
dest[j-1] = '\0';
return;
}
char *message__decode(struct t_account *account,
const char *text)
{
int rc;
regex_t reg;
regmatch_t groups[max_groups];
char msgbuf[100];
char *decoded_text;
const char *cursor;
size_t offset;
if ((rc = regcomp(&reg, format_regex, REG_EXTENDED)))
{
regerror(rc, &reg, msgbuf, sizeof(msgbuf));
weechat_printf(
account->buffer,
_("%s%s: error compiling message formatting regex: %s"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME,
msgbuf);
return strdup(text);
}
decoded_text = malloc(MESSAGE_MAX_LENGTH);
if (!decoded_text)
{
regfree(&reg);
weechat_printf(
account->buffer,
_("%s%s: error allocating space for message"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return strdup(text);
}
decoded_text[0] = '\0';
for (cursor = text; regexec(&reg, cursor, max_groups, groups, 0) == 0; cursor += offset)
{
offset = groups[0].rm_eo;
char *copy = strdup(cursor);
if (!copy)
{
regfree(&reg);
free(decoded_text);
weechat_printf(
account->buffer,
_("%s%s: error allocating space for message"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return strdup(text);
}
copy[groups[1].rm_eo] = '\0';
char *match = strdup(copy + groups[1].rm_so);
if (!match)
{
free(copy);
regfree(&reg);
free(decoded_text);
weechat_printf(
account->buffer,
_("%s%s: error allocating space for message"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return strdup(text);
}
copy[groups[0].rm_so] = '\0';
char *prematch = strdup(copy);
if (!prematch)
{
free(match);
free(copy);
regfree(&reg);
free(decoded_text);
weechat_printf(
account->buffer,
_("%s%s: error allocating space for message"),
weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME);
return strdup(text);
}
free(copy);
strncat(decoded_text, prematch,
MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1);
free(prematch);
char *replacement = message__translate_code(account, match);
free(match);
strncat(decoded_text, replacement,
MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1);
free(replacement);
}
strncat(decoded_text, cursor,
MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1);
message__htmldecode(decoded_text, decoded_text,
MESSAGE_MAX_LENGTH);
regfree(&reg);
return decoded_text;
}

@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_MESSAGE_H_
#define _WEECHAT_XMPP_MESSAGE_H_
#define MESSAGE_MAX_LENGTH 40000
char *message__decode(struct t_account *account,
const char *text);
#endif /*WEECHAT_XMPP_MESSAGE_H*/

1133
omemo.c

File diff suppressed because it is too large Load Diff

@ -0,0 +1,33 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_OMEMO_H_
#define _WEECHAT_XMPP_OMEMO_H_
extern const char *OMEMO_ADVICE;
struct t_omemo
{
struct signal_context *context;
struct signal_protocol_store_context *store_context;
struct t_omemo_db *db;
struct ratchet_identity_key_pair *identity;
uint32_t device_id;
};
void omemo__init(struct t_gui_buffer *buffer, struct t_omemo **omemo,
const char *account_name);
void omemo__serialize(struct t_omemo *omemo, char **device,
char **identity, size_t *identity_len);
void omemo__deserialize(struct t_omemo *omemo, const char *device,
const char *identity, size_t identity_len);
void omemo__free(struct t_omemo *omemo);
#endif /*WEECHAT_XMPP_OMEMO_H*/

388
pgp.c

@ -0,0 +1,388 @@
// This Source Code Form is subject to the terms of the Mozilla PublicAA
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <rnp/rnp.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "pgp.h"
#define RNP_SUCCESS 0
#define PGP_MESSAGE_HEADER "-----BEGIN PGP MESSAGE-----\r\n\r\n"
#define PGP_MESSAGE_FOOTER "\r\n-----END PGP MESSAGE-----\r\n"
#define PGP_SIGNATURE_HEADER "-----BEGIN PGP SIGNATURE-----\r\n\r\n"
#define PGP_SIGNATURE_FOOTER "\r\n-----END PGP SIGNATURE-----\r\n"
const char *PGP_ADVICE = "[PGP encrypted message (XEP-0027)]";
void pgp__init(struct t_pgp **pgp, const char *pub, const char *sec)
{
struct t_pgp *new_pgp;
rnp_input_t keyring;
new_pgp = calloc(1, sizeof(**pgp));
if (rnp_ffi_create(&new_pgp->context,
RNP_KEYSTORE_GPG, RNP_KEYSTORE_GPG) != RNP_SUCCESS) {
return;
}
if (rnp_input_from_path(&keyring, pub) == RNP_SUCCESS) {
if (rnp_load_keys(new_pgp->context, RNP_KEYSTORE_GPG,
keyring, RNP_LOAD_SAVE_PUBLIC_KEYS) == RNP_SUCCESS) {
rnp_input_destroy(keyring);
}
}
if (rnp_input_from_path(&keyring, sec) == RNP_SUCCESS) {
if (rnp_load_keys(new_pgp->context, RNP_KEYSTORE_GPG,
keyring, RNP_LOAD_SAVE_SECRET_KEYS) == RNP_SUCCESS) {
rnp_input_destroy(keyring);
}
}
*pgp = new_pgp;
}
void pgp__free(struct t_pgp *pgp)
{
if (pgp)
{
if (pgp->context)
free(pgp->context);
free(pgp);
}
}
char *pgp__encrypt(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *target, const char *message)
{
rnp_op_encrypt_t encrypt = NULL;
rnp_key_handle_t key = NULL;
rnp_input_t keyfile = NULL;
rnp_input_t input = NULL;
rnp_output_t output = NULL;
char * result = NULL;
rnp_result_t ret;
/* create memory input and file output objects for the message and encrypted message */
if ((ret = rnp_input_from_memory(&input, (uint8_t *)message, strlen(message), false)) !=
RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create input object: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
if ((ret = rnp_output_to_memory(&output, 0)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create output object: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
/* create encryption operation */
if ((ret = rnp_op_encrypt_create(&encrypt, pgp->context, input, output)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create encrypt operation: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
/* setup encryption parameters */
rnp_op_encrypt_set_armor(encrypt, true);
rnp_op_encrypt_set_file_name(encrypt, "message.txt");
rnp_op_encrypt_set_file_mtime(encrypt, time(NULL));
rnp_op_encrypt_set_compression(encrypt, "ZIP", 6);
rnp_op_encrypt_set_cipher(encrypt, RNP_ALGNAME_AES_256);
rnp_op_encrypt_set_aead(encrypt, "None");
/* locate recipient's key and add it to the operation context. */
if ((ret = rnp_locate_key(pgp->context, "keyid", target, &key)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to locate recipient key: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
if ((ret = rnp_op_encrypt_add_recipient(encrypt, key)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to add recipient: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
rnp_key_handle_destroy(key);
key = NULL;
/* locate carbon-copy key and add it to the operation context. */
if ((ret = rnp_locate_key(pgp->context, "keyid", source, &key)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to locate recipient key: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
if ((ret = rnp_op_encrypt_add_recipient(encrypt, key)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to add recipient: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
rnp_key_handle_destroy(key);
key = NULL;
/* execute encryption operation */
if ((ret = rnp_op_encrypt_execute(encrypt)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: encryption failed: %s\n", weechat_prefix("error"), reason);
goto encrypt_finish;
}
uint8_t *buf;
size_t buf_len;
rnp_output_memory_get_buf(output, &buf, &buf_len, false);
result = strndup((char *)buf + strlen(PGP_MESSAGE_HEADER),
buf_len - strlen(PGP_MESSAGE_HEADER) - strlen(PGP_MESSAGE_FOOTER));
encrypt_finish:
rnp_op_encrypt_destroy(encrypt);
rnp_input_destroy(keyfile);
rnp_input_destroy(input);
rnp_output_destroy(output);
rnp_key_handle_destroy(key);
return result;
}
//"hQIMAzlgcSFDGLKEAQ//cGG3DFughC5xBF7xeXz1RdayOfhBAPfoZIq62MVuSnfS\nMfig65Zxz1LtAnnFq90TZY7hiHPBtVlYqg47AbSoYweMdpXsKgbUrd3NNf6k2nsZ\nUkChCtyGuHi8pTzclfle7gT0nNXJ1WcLCZ4ORZCrg3D5A+YTO9tdmE8GQsTT6TdV\nbbxF5yR4JF5SzFhuFL3ZoXPXrWylcwKXarYfoOTa6M2vSsCwApVIXQgJ/FI46sLT\nb0B/EVCjFvcvjkNr7+K7mQtth+x0a0pC4BtEhRvnIRAe/sdGp8NY+DP76clx4U+k\nIDG4H92F632pR6eEIoZttnBoaj0O4sTVAJCao5AoecR4w2FDqBWWtIyQp5vbo17/\nMtzungkk5vQP6Jhu36wa+JKpbHoxomVpHPZfAtIoyaY6pzQ0bUomIlSVpbZDvF68\nZKTlFd89Pm5x0JO5gsVYvf+N9Ed33d34n/0CFz5K5Tgu4Bk0v4LWEy3wtNsuQB4p\nkBSZJk7I2BakcRwP0zwld6rRHFIX1pb7zqThBPZGB9RkWPltiktUTibOII12tWhi\nksFpQJ8l1A8h9vM5kUXIeD6H2yP0CBUEIZF3Sf+jiSRZ/1/n3KoUrKEzkf/y4xgv\n1LA4pMjNLEr6J2fqGyYRFv4Bxv3PIvF17V5CwOtguxGRJHJXdIzm1BSHSqXxHezS\nYAFXMUb9fw3QX7Ed23KiyZjzd/LRsQBqMs9RsYyZB2PqF9x84lQYYbE8lErrryvK\nUEtmJKPw3Hvb7kgGox5vl5+KCg9q64EU9TgQpufYNShKtDz7Fsvc+ncgZoshDUeo\npw==\n=euIB"
char *pgp__decrypt(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *ciphertext)
{
rnp_input_t input = NULL;
rnp_output_t output = NULL;
uint8_t * buf = NULL;
size_t buf_len = 0;
char * result = NULL;
rnp_result_t ret;
buf_len = strlen(PGP_MESSAGE_HEADER) + strlen(ciphertext) + strlen(PGP_MESSAGE_FOOTER) + 1;
buf = malloc(sizeof(char) * buf_len);
buf_len = snprintf((char *)buf, buf_len, PGP_MESSAGE_HEADER "%s" PGP_MESSAGE_FOOTER, ciphertext);
/* create file input and memory output objects for the encrypted message and decrypted
* message */
if ((ret = rnp_input_from_memory(&input, buf, buf_len, false)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create input object: %s\n", weechat_prefix("error"), reason);
goto decrypt_finish;
}
if ((ret = rnp_output_to_memory(&output, 0)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create output object: %s\n", weechat_prefix("error"), reason);
goto decrypt_finish;
}
if ((ret = rnp_decrypt(pgp->context, input, output)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: public-key decryption failed: %s\n", weechat_prefix("error"), reason);
goto decrypt_finish;
}
free(buf);
/* get the decrypted message from the output structure */
if (rnp_output_memory_get_buf(output, &buf, &buf_len, false) != RNP_SUCCESS) {
goto decrypt_finish;
}
result = strndup((const char *)buf, (int)buf_len);
decrypt_finish:
rnp_input_destroy(input);
rnp_output_destroy(output);
return result;
}
char *pgp__verify(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *certificate)
{
rnp_op_verify_t verify = NULL;
rnp_input_t input = NULL;
rnp_input_t signature = NULL;
uint8_t * buf = NULL;
size_t buf_len = 0;
size_t sigcount = 0;
char * result = NULL;
rnp_result_t ret;
buf_len = strlen(PGP_SIGNATURE_HEADER) + strlen(certificate) + strlen(PGP_SIGNATURE_FOOTER) + 1;
buf = malloc(sizeof(char) * buf_len);
buf_len = snprintf((char *)buf, buf_len, PGP_SIGNATURE_HEADER "%s" PGP_SIGNATURE_FOOTER, certificate);
/* create file input memory objects for the signed message and verified message */
if ((ret = rnp_input_from_memory(&input, buf, buf_len, false)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create input object: %s\n", weechat_prefix("error"), reason);
goto verify_finish;
}
if ((ret = rnp_input_from_memory(&signature, buf, buf_len, false)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create input object: %s\n", weechat_prefix("error"), reason);
goto verify_finish;
}
if ((ret = rnp_op_verify_detached_create(&verify, pgp->context, input, signature)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to create verification context: %s\n", weechat_prefix("error"), reason);
goto verify_finish;
}
//if ((
ret = rnp_op_verify_execute(verify)
;
// ) != RNP_ERROR_SIGNATURE_INVALID)
// if (ret != RNP_ERROR_SIGNATURE_INVALID) {
// const char *reason = rnp_result_to_string(ret);
// weechat_printf(buffer, "%spgp: failed to execute verification operation: %s\n", weechat_prefix("error"), reason);
// goto verify_finish;
// }
/* now check signatures and get some info about them */
if ((ret = rnp_op_verify_get_signature_count(verify, &sigcount)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get signature count: %s\n", weechat_prefix("error"), reason);
goto verify_finish;
}
for (size_t i = 0; i < sigcount; i++) {
rnp_op_verify_signature_t sig = NULL;
rnp_key_handle_t key = NULL;
rnp_signature_handle_t signature = NULL;
char * keyid = NULL;
if ((ret = rnp_op_verify_get_signature_at(verify, i, &sig)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get signature %d: %s\n", weechat_prefix("error"), (int)i, reason);
goto verify_finish;
}
if ((ret = rnp_op_verify_signature_get_key(sig, &key)) == RNP_SUCCESS) {
if ((ret = rnp_key_get_keyid(key, &keyid)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get key id %d: %s\n", weechat_prefix("error"), (int)i, reason);
rnp_key_handle_destroy(key);
goto verify_finish;
}
if ((ret = rnp_key_get_signature_at(key, 0, &signature)) == RNP_SUCCESS) {
if ((ret = rnp_signature_get_keyid(signature, &keyid)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get key id: %s\n", weechat_prefix("error"), reason);
rnp_key_handle_destroy(key);
goto verify_finish;
}
rnp_signature_handle_destroy(signature);
}
} else {
if ((ret = rnp_op_verify_signature_get_handle(sig, &signature)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get signature's %d handle: %s\n", weechat_prefix("error"), (int)i, reason);
goto verify_finish;
}
if ((ret = rnp_signature_get_keyid(signature, &keyid)) != RNP_SUCCESS) {
const char *reason = rnp_result_to_string(ret);
weechat_printf(buffer, "%spgp: failed to get key id: %s\n", weechat_prefix("error"), reason);
rnp_key_handle_destroy(key);
goto verify_finish;
}
}
result = strdup(keyid);
rnp_buffer_destroy(keyid);
rnp_key_handle_destroy(key);
break;
}
verify_finish:
rnp_op_verify_destroy(verify);
rnp_input_destroy(input);
rnp_input_destroy(signature);
return result;
}
char *pgp__sign(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *message)
{
rnp_input_t keyfile = NULL;
rnp_input_t input = NULL;
rnp_output_t output = NULL;
rnp_op_sign_t sign = NULL;
rnp_key_handle_t key = NULL;
uint8_t * buf = NULL;
size_t buf_len = 0;
char * result = NULL;
/* create file input and memory output objects for the encrypted message and decrypted
* message */
if (rnp_input_from_memory(&input, (uint8_t *)message, strlen(message), false) !=
RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to create input object\n", weechat_prefix("error"));
goto sign_finish;
}
if (rnp_output_to_memory(&output, 0) != RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to create output object\n", weechat_prefix("error"));
goto sign_finish;
}
/* initialize and configure sign operation */
if (rnp_op_sign_detached_create(&sign, pgp->context, input, output) != RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to create sign operation\n", weechat_prefix("error"));
goto sign_finish;
}
/* armor, file name, compression */
rnp_op_sign_set_armor(sign, true);
rnp_op_sign_set_file_name(sign, "message.txt");
rnp_op_sign_set_file_mtime(sign, time(NULL));
rnp_op_sign_set_compression(sign, "ZIP", 6);
/* signatures creation time - by default will be set to the current time as well */
rnp_op_sign_set_creation_time(sign, time(NULL));
/* signatures expiration time - by default will be 0, i.e. never expire */
rnp_op_sign_set_expiration_time(sign, 365 * 24 * 60 * 60);
/* set hash algorithm - should be compatible for all signatures */
rnp_op_sign_set_hash(sign, RNP_ALGNAME_SHA256);
/* now add signatures. First locate the signing key, then add and setup signature */
if (rnp_locate_key(pgp->context, "keyid", source, &key) != RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to locate signing key: %s\n", weechat_prefix("error"), source);
goto sign_finish;
}
if (rnp_op_sign_add_signature(sign, key, NULL) != RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to add signature for key: %s\n", weechat_prefix("error"), source);
goto sign_finish;
}
rnp_key_handle_destroy(key);
key = NULL;
/* finally do signing */
if (rnp_op_sign_execute(sign) != RNP_SUCCESS) {
weechat_printf(buffer, "%spgp: failed to sign with key: %s\n", weechat_prefix("error"), source);
goto sign_finish;
}
/* get the signature from the output structure */
if (rnp_output_memory_get_buf(output, &buf, &buf_len, false) != RNP_SUCCESS) {
goto sign_finish;
}
result = strndup((char *)buf + strlen(PGP_SIGNATURE_HEADER),
buf_len - strlen(PGP_SIGNATURE_HEADER) - strlen(PGP_SIGNATURE_FOOTER));
sign_finish:
rnp_input_destroy(keyfile);
rnp_key_handle_destroy(key);
rnp_op_sign_destroy(sign);
rnp_input_destroy(input);
rnp_output_destroy(output);
return result;
}

28
pgp.h

@ -0,0 +1,28 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_PGP_H_
#define _WEECHAT_XMPP_PGP_H_
extern const char *PGP_ADVICE;
struct t_pgp
{
struct rnp_ffi_st *context;
const char *keyid;
};
void pgp__init(struct t_pgp **pgp, const char *pub, const char *sec);
void pgp__free(struct t_pgp *pgp);
char *pgp__decrypt(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *ciphertext);
char *pgp__encrypt(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *target, const char *message);
char *pgp__verify(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *certificate);
char *pgp__sign(struct t_gui_buffer *buffer, struct t_pgp *pgp, const char *source, const char *message);
#endif /*WEECHAT_XMPP_PGP_H*/

@ -14,6 +14,7 @@ namespace c {
#include <time.h> #include <time.h>
#include <strophe.h> #include <strophe.h>
#include "plugin.h"
#include "config.h" #include "config.h"
#include "account.h" #include "account.h"
#include "connection.h" #include "connection.h"
@ -21,6 +22,16 @@ namespace c {
#include "input.h" #include "input.h"
#include "buffer.h" #include "buffer.h"
#include "completion.h" #include "completion.h"
struct t_weechat_plugin *weechat_xmpp_plugin() {
return weechat_plugin;
};
const char *weechat_xmpp_plugin_name() {
return weechat::plugin::instance.name().data();
};
const char *weechat_xmpp_plugin_version() {
return weechat::plugin::instance.version().data();
};
} }
#define TIMER_INTERVAL_SEC 0.01 #define TIMER_INTERVAL_SEC 0.01
@ -99,11 +110,11 @@ namespace weechat {
bool plugin::init(std::vector<std::string>) { bool plugin::init(std::vector<std::string>) {
weechat_printf(nullptr, "%s: It works!", this->name().data()); weechat_printf(nullptr, "%s: It works!", this->name().data());
return weechat_plugin_init(); return c::weechat_plugin_init();
} }
bool plugin::end() { bool plugin::end() {
weechat_plugin_end(); c::weechat_plugin_end();
return true; return true;
} }

@ -0,0 +1,18 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_PLUGIN_H_
#define _WEECHAT_XMPP_PLUGIN_H_
#ifndef __cplusplus
#define weechat_plugin weechat_xmpp_plugin()
#define WEECHAT_XMPP_PLUGIN_NAME weechat_xmpp_plugin_name()
#define WEECHAT_XMPP_PLUGIN_VERSION weechat_xmpp_plugin_version()
#endif//__cplusplus
struct t_weechat_plugin *weechat_xmpp_plugin();
const char *weechat_xmpp_plugin_name();
const char *weechat_xmpp_plugin_version();
#endif /*WEECHAT_XMPP_PLUGIN_H*/

255
user.c

@ -0,0 +1,255 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "account.h"
#include "user.h"
#include "channel.h"
const char *user__get_colour(struct t_user *user)
{
return weechat_info_get("nick_color", user->profile.display_name);
}
const char *user__get_colour_for_nicklist(struct t_user *user)
{
return weechat_info_get("nick_color_name", user->profile.display_name);
}
const char *user__as_prefix_raw(struct t_account *account,
const char *name)
{
static char result[2048];
(void) account;
snprintf(result, sizeof(result), "%s%s%s",
weechat_info_get("nick_color", name),
name, weechat_color("reset"));
return result;
}
const char *user__as_prefix(struct t_account *account,
struct t_user *user,
const char *name)
{
static char result[2048];
(void) account;
snprintf(result, sizeof(result), "%s%s\t",
user__get_colour(user),
name ? name : user->profile.display_name);
return result;
}
struct t_user *user__bot_search(struct t_account *account,
const char *pgp_id)
{
struct t_user *ptr_user;
if (!account || !pgp_id)
return NULL;
for (ptr_user = account->users; ptr_user;
ptr_user = ptr_user->next_user)
{
if (ptr_user->profile.pgp_id &&
weechat_strcasecmp(ptr_user->profile.pgp_id, pgp_id) == 0)
return ptr_user;
}
return NULL;
}
struct t_user *user__search(struct t_account *account,
const char *id)
{
struct t_user *ptr_user;
if (!account || !id)
return NULL;
for (ptr_user = account->users; ptr_user;
ptr_user = ptr_user->next_user)
{
if (weechat_strcasecmp(ptr_user->id, id) == 0)
return ptr_user;
}
return NULL;
}
void user__nicklist_add(struct t_account *account,
struct t_channel *channel,
struct t_user *user)
{
struct t_gui_nick_group *ptr_group;
struct t_gui_buffer *ptr_buffer;
char *name = channel ? user->profile.display_name : user->id;
if (channel && weechat_strcasecmp(xmpp_jid_bare(account->context, name),
channel->id) == 0)
name = xmpp_jid_resource(account->context, name);
ptr_buffer = channel ? channel->buffer : account->buffer;
char *group = "...";
if (weechat_strcasecmp(user->profile.affiliation, "outcast") == 0)
group = "!";
if (weechat_strcasecmp(user->profile.role, "visitor") == 0)
group = "?";
if (weechat_strcasecmp(user->profile.role, "participant") == 0)
group = "+";
if (weechat_strcasecmp(user->profile.affiliation, "member") == 0)
group = "%";
if (weechat_strcasecmp(user->profile.role, "moderator") == 0)
group = "@";
if (weechat_strcasecmp(user->profile.affiliation, "admin") == 0)
group = "&";
if (weechat_strcasecmp(user->profile.affiliation, "owner") == 0)
group = "~";
ptr_group = weechat_nicklist_search_group(ptr_buffer, NULL, group);
weechat_nicklist_add_nick(ptr_buffer, ptr_group,
name,
user->is_away ?
"weechat.color.nicklist_away" :
user__get_colour_for_nicklist(user),
group,
"bar_fg",
1);
}
void user__nicklist_remove(struct t_account *account,
struct t_channel *channel,
struct t_user *user)
{
struct t_gui_nick *ptr_nick;
struct t_gui_buffer *ptr_buffer;
char *name = user->profile.display_name;
if (channel && weechat_strcasecmp(xmpp_jid_bare(account->context, name),
channel->id) == 0)
name = xmpp_jid_resource(account->context, name);
ptr_buffer = channel ? channel->buffer : account->buffer;
if ((ptr_nick = weechat_nicklist_search_nick(ptr_buffer, NULL, name)))
weechat_nicklist_remove_nick(ptr_buffer, ptr_nick);
}
struct t_user *user__new(struct t_account *account,
const char *id, const char *display_name)
{
struct t_user *new_user, *ptr_user;
if (!account || !id)
{
return NULL;
}
if (!account->users)
channel__add_nicklist_groups(account, NULL);
ptr_user = user__search(account, id);
if (ptr_user)
{
user__nicklist_add(account, NULL, ptr_user);
return ptr_user;
}
if ((new_user = malloc(sizeof(*new_user))) == NULL)
{
return NULL;
}
new_user->prev_user = account->last_user;
new_user->next_user = NULL;
if (account->last_user)
(account->last_user)->next_user = new_user;
else
account->users = new_user;
account->last_user = new_user;
new_user->id = strdup(id);
new_user->name = NULL;
new_user->profile.avatar_hash = NULL;
new_user->profile.status_text = NULL;
new_user->profile.status = NULL;
new_user->profile.idle = NULL;
new_user->profile.display_name = display_name ?
strdup(display_name) : strdup("");
new_user->profile.affiliation = NULL;
new_user->profile.email = NULL;
new_user->profile.role = NULL;
new_user->profile.pgp_id = NULL;
new_user->updated = 0;
new_user->is_away = 0;
user__nicklist_add(account, NULL, new_user);
return new_user;
}
void user__free(struct t_account *account,
struct t_user *user)
{
struct t_user *new_users;
if (!account || !user)
return;
/* remove user from users list */
if (account->last_user == user)
account->last_user = user->prev_user;
if (user->prev_user)
{
(user->prev_user)->next_user = user->next_user;
new_users = account->users;
}
else
new_users = user->next_user;
if (user->next_user)
(user->next_user)->prev_user = user->prev_user;
/* free user data */
if (user->id)
free(user->id);
if (user->name)
free(user->name);
if (user->profile.avatar_hash)
free(user->profile.avatar_hash);
if (user->profile.status_text)
free(user->profile.status_text);
if (user->profile.status)
free(user->profile.status);
if (user->profile.idle)
free(user->profile.idle);
if (user->profile.display_name)
free(user->profile.display_name);
if (user->profile.affiliation)
free(user->profile.affiliation);
if (user->profile.email)
free(user->profile.email);
if (user->profile.role)
free(user->profile.role);
free(user);
account->users = new_users;
}
void user__free_all(struct t_account *account)
{
while (account->users)
user__free(account, account->users);
}

@ -0,0 +1,60 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_USER_H_
#define _WEECHAT_XMPP_USER_H_
struct t_user_profile
{
char *avatar_hash;
char *status_text;
char *status;
char *idle;
char *display_name;
char *email;
char *role;
char *affiliation;
char *pgp_id;
};
struct t_user
{
char *id;
char *name;
struct t_user_profile profile;
int updated;
int is_away;
struct t_user *prev_user;
struct t_user *next_user;
};
struct t_channel;
const char *user__get_colour(struct t_user *user);
const char *user__as_prefix_raw(struct t_account *account,
const char *name);
const char *user__as_prefix(struct t_account *account,
struct t_user *user,
const char *name);
struct t_user *user__search(struct t_account *account,
const char *id);
struct t_user *user__new(struct t_account *account,
const char *id, const char *display_name);
void user__free_all(struct t_account *account);
void user__nicklist_add(struct t_account *account,
struct t_channel *channel,
struct t_user *user);
void user__nicklist_remove(struct t_account *account,
struct t_channel *channel,
struct t_user *user);
#endif /*WEECHAT_XMPP_USER_H*/

@ -0,0 +1,49 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdio.h>
#include <stdlib.h>
#include <strophe.h>
#include <weechat/weechat-plugin.h>
#include "plugin.h"
#include "util.h"
int char_cmp(const void *p1, const void *p2)
{
return *(const char *)p1 == *(const char *)p2;
}
char *exec(const char *command)
{
// use hook_process instead!
char buffer[128];
char **result = weechat_string_dyn_alloc(256);
// Open pipe to file
FILE* pipe = popen(command, "r");
if (!pipe) {
return "popen failed!";
}
// read till end of process:
while (!feof(pipe)) {
// use buffer to read and add to result
if (fgets(buffer, 128, pipe) != NULL)
weechat_string_dyn_concat(result, buffer, -1);
}
pclose(pipe);
weechat_string_dyn_free(result, 0);
return *result;
}
char *stanza_xml(xmpp_stanza_t *stanza)
{
char *result;
size_t len;
xmpp_stanza_to_text(stanza, &result, &len);
return result;
}

@ -0,0 +1,14 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_UTIL_H_
#define _WEECHAT_XMPP_UTIL_H_
int char_cmp(const void *p1, const void *p2);
char *exec(const char *command);
char *stanza_xml(struct _xmpp_stanza_t *stanza);
#endif /*WEECHAT_XMPP_UTIL_H*/

@ -0,0 +1,403 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <strophe.h>
#include "stanza.h"
xmpp_stanza_t *stanza__iq(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, char *ns, char *id,
char *from, char *to, char *type)
{
xmpp_stanza_t *parent = base ? base : xmpp_iq_new(context, type, id);
xmpp_stanza_t **child = children;
if (ns)
{
xmpp_stanza_set_ns(parent, ns);
free(ns);
}
if (base && id)
{
xmpp_stanza_set_id(parent, id);
free(id);
}
if (from)
{
xmpp_stanza_set_from(parent, from);
free(from);
}
if (to)
{
xmpp_stanza_set_to(parent, to);
free(to);
}
if (base && type)
{
xmpp_stanza_set_attribute(parent, "type", type);
free(type);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child);
++child;
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "pubsub");
}
if (ns)
{
xmpp_stanza_set_ns(parent, ns->value);
ns->finalize(ns);
free(ns);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child);
++child;
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, char *node)
{
xmpp_stanza_t *parent = base;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "items");
}
if (node)
{
xmpp_stanza_set_attribute(parent, "node", node);
free(node);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *node)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "publish");
}
if (node)
{
xmpp_stanza_set_attribute(parent, "node", node->value);
node->finalize(node);
free(node);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child);
++child;
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *id)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "item");
}
if (id)
{
xmpp_stanza_set_id(parent, id->value);
id->finalize(id);
free(id);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child);
++child;
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "list");
}
if (ns)
{
xmpp_stanza_set_ns(parent, ns->value);
ns->finalize(ns);
free(ns);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *id)
{
xmpp_stanza_t *parent = base;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "device");
}
if (id)
{
xmpp_stanza_set_id(parent, id->value);
id->finalize(id);
free(id);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "bundle");
}
if (ns)
{
xmpp_stanza_set_ns(parent, ns->value);
ns->finalize(ns);
free(ns);
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_signedPreKeyPublic(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *signedPreKeyId)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "signedPreKeyPublic");
}
if (signedPreKeyId)
{
xmpp_stanza_set_attribute(parent, "signedPreKeyId", signedPreKeyId->value);
signedPreKeyId->finalize(signedPreKeyId);
free(signedPreKeyId);
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_signedPreKeySignature(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "signedPreKeySignature");
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_identityKey(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "identityKey");
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_prekeys(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "prekeys");
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *preKeyId)
{
xmpp_stanza_t *parent = base;
xmpp_stanza_t **child = children;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "preKeyPublic");
}
if (preKeyId)
{
xmpp_stanza_set_attribute(parent, "preKeyId", preKeyId->value);
preKeyId->finalize(preKeyId);
free(preKeyId);
}
while (child && *child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child++);
}
return parent;
}
xmpp_stanza_t *stanza__iq_enable(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *ns)
{
xmpp_stanza_t *parent = base;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "enable");
}
if (ns)
{
xmpp_stanza_set_ns(parent, ns->value);
ns->finalize(ns);
free(ns);
}
return parent;
}
xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *ns)
{
xmpp_stanza_t *parent = base;
if (!parent)
{
parent = xmpp_stanza_new(context);
xmpp_stanza_set_name(parent, "ping");
}
if (ns)
{
xmpp_stanza_set_ns(parent, ns->value);
ns->finalize(ns);
free(ns);
}
return parent;
}

@ -0,0 +1,48 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <stdlib.h>
#include <strophe.h>
xmpp_stanza_t *stanza__presence(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, char *ns,
char *from, char *to, char *type)
{
xmpp_stanza_t *parent = base ? base : xmpp_presence_new(context);
xmpp_stanza_t **child = children;
if (ns)
{
xmpp_stanza_set_ns(parent, ns);
free(ns);
}
if (from)
{
xmpp_stanza_set_from(parent, from);
free(from);
}
if (to)
{
xmpp_stanza_set_to(parent, to);
free(to);
}
if (type)
{
xmpp_stanza_set_attribute(parent, "type", type);
free(type);
}
while (*child)
{
xmpp_stanza_add_child(parent, *child);
xmpp_stanza_release(*child);
++child;
}
return parent;
}

@ -0,0 +1,101 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef _WEECHAT_XMPP_STANZA_H_
#define _WEECHAT_XMPP_STANZA_H_
struct t_string
{
char *value;
void (*finalize)(struct t_string *);
void *pointer;
};
static void t_string_noop(struct t_string *string)
{ (void)string; }
static void t_string_free(struct t_string *string)
{ free(string->value); }
static void t_string_xmpp_free(struct t_string *string)
{ xmpp_free(string->pointer, string->value); }
static inline struct t_string *with_noop(const char *const value)
{
struct t_string *string = malloc(sizeof(struct t_string));
string->value = (char*)value;
string->finalize = &t_string_noop;
string->pointer = NULL;
return string;
}
static inline struct t_string *with_free(char *value)
{
struct t_string *string = malloc(sizeof(struct t_string));
string->value = value;
string->finalize = &t_string_free;
string->pointer = NULL;
return string;
}
static inline struct t_string *with_xmpp_free(char *value, xmpp_ctx_t *pointer)
{
struct t_string *string = malloc(sizeof(struct t_string));
string->value = value;
string->finalize = &t_string_xmpp_free;
string->pointer = pointer;
return string;
}
xmpp_stanza_t *stanza__presence(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, const char *ns,
char *from, char *to, const char *type);
xmpp_stanza_t *stanza__iq(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, char *ns, char *id,
char *from, char *to, char *type);
xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns);
xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, char *node);
xmpp_stanza_t *stanza__iq_pubsub_publish(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *node);
xmpp_stanza_t *stanza__iq_pubsub_publish_item(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *id);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *id);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle(xmpp_ctx_t *context, xmpp_stanza_t *base,
xmpp_stanza_t **children, struct t_string *ns);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_signedPreKeyPublic(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *signedPreKeyId);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_signedPreKeySignature(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_identityKey(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_prekeys(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children);
xmpp_stanza_t *stanza__iq_pubsub_publish_item_bundle_prekeys_preKeyPublic(
xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, struct t_string *preKeyId);
xmpp_stanza_t *stanza__iq_enable(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *ns);
xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base,
struct t_string *ns);
#endif /*WEECHAT_XMPP_STANZA_H*/
Loading…
Cancel
Save