250 Commits

Author SHA1 Message Date
f9848ed627 Update README 2021-10-31 05:16:57 +01:00
686a39df38 CMakeLists.txt: slightly modernize 2021-10-31 04:30:04 +01:00
9cea3fca91 Update NEWS 2021-10-30 14:25:13 +02:00
5165f76b7c xC: quote text coming from a bracketed paste
Not having this has caused me much annoyance over the years.
2021-10-30 09:27:32 +02:00
92ac13f3c6 xC: allow passing the cursor position to editors
Add a configuration option to set a custom editor command,
different from EDITOR or VISUAL--those remain as defaults.

Implement substitutions allowing to convey cursor information
to VIM and Emacs (the latter of which is fairly painful to cater to),
and put usage hints in the configuration option's description.

This should make the editing experience a bit more seamless
for users, even though the position is carried over in one way only.

No sophisticated quoting capabilities were deemed necessary,
it is a lot of code already.  The particular syntax is inspired
by .desktop files and systemd.

["/bin/sh", "-c", "vim +$2go \"$1\"", filename, position, line, column]
would be a slightly simpler but cryptic way of implementing this.
2021-10-30 09:02:35 +02:00
df4ca74580 xC: make libedit autocomplete less miserable
Omitting even this hack was a huge hit to overall usability.
2021-10-30 08:29:16 +02:00
9e297244a4 Update .gitignore 2021-10-30 03:37:22 +02:00
d32ba133c0 Add clang-format configuration, clean up 2021-10-30 02:55:19 +02:00
ce3976e1ec xC: normalize ^J behaviour to follow Readline
For some reason Editline inserts it verbatim,
but in a more broken manner than it has with ^V^J.
2021-10-28 08:49:01 +02:00
e5ed89646b xC: fix newer libedit (2021-08-29) 2021-10-28 08:23:52 +02:00
5e728f6d31 Bump version, update NEWS 2021-10-06 14:05:23 +02:00
766f68e070 Bump liberty 2021-10-06 13:52:59 +02:00
3dc5242d43 Bump liberty
Importing some minor unimportant fixes.
2021-09-26 08:55:46 +02:00
fd9d5db1d2 xD: bump the soft file descriptor limit
By default it's a mere thousand connections, which is unnecessarily
crippling our advertised ability to handle lots of them.

Thanks for the advice, Lennart.
2021-09-23 20:32:00 +02:00
cb480b4c71 xC: show orphan outcoming actions differently
It's hard to think of anything actually good here.

This would be an exceptionally rare thing to do, anyway.
2021-09-05 02:51:05 +02:00
59cc423694 xC: abandon Freenode, embrace IRCnet
You're not fucking supposed to require a fucking registration
on fucking IRC networks.
2021-08-29 15:18:20 +02:00
9323089d66 xC: mIRC didn't invent all IRC formatting
So let's not confuse ourselves.
2021-08-29 12:12:52 +02:00
de7df1f60d xC: refactor parsing of IRC formatting 2021-08-29 12:06:53 +02:00
b082e82b62 xC: fix displaying IRC colours above 16
First, we indexed the colour array without a required offset.
Second, the data type was too small and overflowed negative.

Detected during a refactor, which this is a part of.
2021-08-28 18:25:03 +02:00
b8dbc70a9c xC: respect text formatting when autosplitting 2021-08-28 18:24:20 +02:00
e0ad67a921 Bump version, update NEWS 2021-08-07 07:53:08 +02:00
565edc15b4 README.adoc: be consistent in emphasizing 2021-08-07 07:40:02 +02:00
5d285ffb96 xB: fix up the special IPC command's name
To reflect the new disorder.
2021-08-06 17:18:06 +02:00
50057d5149 Come up with sillier names for the binaries
I'm not entirely sure, but it looks like some people might not like
jokes about the Holocaust.

On a more serious note, the project has become more serious over
the 7 or so years of its existence.
2021-08-06 16:43:59 +02:00
1f64710e79 NEWS: improve wording
The phrase "input line" has already been used once in the file.
2021-07-24 09:40:35 +02:00
027bf8666e degesch: never bump our own chanuser
With IRCv3.2 echo-message, each successfully sent message would
move us to the front of the list used for chanuser autocomplete.

Such behaviour seems useless.

Also abandon the idea of bumping on other kinds of messages.
2021-07-24 09:27:49 +02:00
7c7e12d8d5 degesch: start with lexically ordered chanusers
This makes nick autocompletion start in a non-arbitrary state.
2021-07-23 19:14:57 +02:00
3cb93d24e8 degesch: order nick autocomplete by time 2021-07-23 18:43:20 +02:00
acddfe2cfa degesch: cleanup 2021-07-23 18:43:19 +02:00
051c43a072 NEWS: fix a garbled up entry
Try not to commit, push and tag releases tired.
2021-07-08 05:17:13 +02:00
0fe0b56280 Bump version, update NEWS 2021-07-08 05:09:30 +02:00
f0281cf028 test-nick-colors: fix and streamline
A recent addition of an N_ELEMENTS macro invocation broke it.
2021-06-25 06:35:00 +02:00
da5dd4eb91 degesch: make /ban and /unban respect EXTBAN 2021-06-17 12:21:48 +02:00
10cb6651c0 degesch: expand/analyze a few TODO comments 2021-06-16 22:10:25 +02:00
7f28dcd1ef degesch: make "/help /command" work
Works for aliases as well.  Resolves a TODO entry.
2021-06-16 21:57:47 +02:00
61c52d793c degesch: fix a GCC compiler warning 2021-06-15 07:11:35 +02:00
b4dd0052ff degesch: pick colours based on relative luminance
Replaces the inaccurate Rec. 709 luma we used to use before.

This is the first feature here that requires libm, which doesn't
seem to be a particularly great sacrifice.

Moreover, I've rectified that the input isn't linear in sRGB,
and then was even normalized wrong for the luma formula.
2021-06-15 07:09:23 +02:00
e3c47c33fa degesch: implement -=/+= for multiple values
It didn't make sense to have these unimplemented,
though perhaps += shouldn't enforce a set.

Sadly, autocomplete is fairly difficult for -= of multiple items.
2021-06-14 09:06:38 +02:00
80c1e8f8eb degesch: make /deop and /devoice default to self
It's pretty annoying to type `/mode -o <user>`, for little reason.
2021-06-03 00:12:22 +02:00
c5f49ab1e6 censor.lua: strip colours, configurable formatting
Colour parsing code taken from prime.lua, and modified to strip.
2021-06-03 00:12:22 +02:00
6f62b9c0c7 degesch: make CHGHOST update our own userhost info
I've almost forgotten that we use this for message spliting.
2021-05-30 08:23:23 +02:00
c1d69e3630 degesch: add support for IRCv3 chghost
This is somewhat similar to a nick change.
2021-05-30 08:06:38 +02:00
c75ef167f2 degesch: document the SASL EXTERNAL support
So far it's only been mentioned in the NEWS file,
which is definitely not sufficient.

It would be good to move this kind of stuff out from README.adoc.
2021-05-29 06:38:33 +02:00
ddffc71abe degesch: factor out irc_try_finish_cap_negotiation()
Too much repeated, non-obvious code.
2021-05-28 04:59:21 +02:00
5a0b2d1c57 degesch: add trivial SASL EXTERNAL support
Just set `tls_cert`, and add `sasl` to `capabilities`.
2021-05-28 04:59:20 +02:00
bb451a5050 degesch: support CAP DEL, request cap-notify
It doesn't require much effort to cancel capabilities, plus with
the newer version we get the respective notification anyway.
2021-05-28 04:59:20 +02:00
61f15ead8a degesch: don't CAP REQ when already registered
The list may later be requested manually, which shouldn't have
an unexpected side-effect.
2021-05-28 04:59:20 +02:00
17f430043a degesch: IRCv3.2 capability negotiation
We can receive and display capability values now.
2021-05-28 04:59:20 +02:00
735096d76d degesch: add a /squery command for IRCnet 2021-05-28 04:06:27 +02:00
1ba59e6ee0 degesch: fix back-parsing outgoing CAP REQ
The bug has apparently been there since the beginning.
2021-05-28 04:04:44 +02:00
f9ba682c0e degesch: reset away-notify on disconnect
Forgotten to do it when adding the support for it.
2021-05-28 04:04:23 +02:00
8e8ffe2c73 degesch: don't switch to channels while typing
We might just always set the highlighted bit on,
it would be consistent with PMs.
2021-04-10 05:11:46 +02:00
d05c85833d degesch: make a second SIGINT force-quit
Also fixed the possibility of eating a sequence of signals
as we reset the indicators /after/ we took action,
which creates a time window for races.
2020-11-01 15:33:16 +01:00
2336340ad8 Bump version, update NEWS 2020-10-31 23:50:32 +01:00
8f5dec0456 degesch: buffer creation cleanup 2020-10-31 23:44:18 +01:00
3dc6ee9a5b degesch: sanitize IRC nicknames/channel names
Don't trust the IRCd to have them in a subset of UTF-8.
2020-10-31 23:25:08 +01:00
821ce04915 degesch: implement autocompletion for /set
It was super annoying to just slightly modify strings and
string arrays, now you can have existing values filled in.

complete_word() looks a bit cleaner now as well.
2020-10-31 23:18:31 +01:00
2fe3b95ecd README.adoc: improve backlog helper invocation
When fancy-prompt.lua is enabled, tho prompt is two-lined
and a simple PageUp would skip one line of content.

It works slightly better than it should: when there's under
a page of content to scroll, there is no shift at all.
2020-10-31 20:00:23 +01:00
32c99c9d66 kike: avoid crash with a wildcard address
A most unfortunate 06d3b3b regression, mostly stemming from
forgetting why the `break` was in place and not documenting it.
2020-10-31 17:34:32 +01:00
cd7133e173 README.adoc: minor documentation update 2020-10-31 16:06:13 +01:00
b4ed52015a degesch: mark some issues for later resolution 2020-10-31 16:06:12 +01:00
271689da99 fancy-prompt.lua: allow non-ASCII buffer names
It may theoretically bite us in the ass with non-UTF-8-compliant
IRC servers, and certainly with double-width characters.
2020-10-31 16:05:15 +01:00
38c23d0d38 degesch: fix fancy-prompt.lua with libedit
Partly by unifying the interface for prompt hooks to match GNU Readline.
2020-10-31 16:04:30 +01:00
439af8884c degesch: make PageUp actually scroll a page up
Now that the input to the backlog helper is wrapped the same way
as what we display.  There's a slight issue always triggered by
fancy-prompt.lua where a multiline prompt/command line makes less(1)
go too high up but it's nothing too important.
2020-10-31 16:00:55 +01:00
8ccf38ad76 Minor rebranding
There's nothing experimental about this project anymore.  It's stable.

Maybe we should add a photo of Hitler or something.
2020-10-31 13:42:56 +01:00
47a4c8beca CMakeLists.txt: clean up OpenBSD support
A few things might have changed.
2020-10-29 15:27:09 +01:00
1de4a2ae34 Bump version, update NEWS 2020-10-29 03:03:07 +01:00
53cc52e320 Add real manual pages
To some extent they duplicate the README but from a different angle.
2020-10-29 02:46:40 +01:00
cbe4009308 degesch: fix Lua 5.3 build
Regression from the last release.
2020-10-29 02:44:40 +01:00
06d3b3bd2b kike: ensure NULL binds to both IPv4 and IPv6 2020-10-29 00:39:57 +01:00
132e4a38b8 kike: document the "operators" setting usefully
Now our user just needs to be able to guess that it's a hex string.
2020-10-28 23:53:03 +01:00
8429995cb6 ZyklonB: don't look for plugins in /usr/lib
It's quite unlikely that this project will ever see compiled plugins.
2020-10-28 17:17:48 +01:00
03ed097353 ZyklonB: use XDG paths by default
Install plugins to /usr/share rather than /usr/lib since they're
arch-independent.  Many precedents can be found for scripted plugins
in /usr/share and fewer for /usr/lib.

Look for plugins in all XDG data directories and repurpose
the "plugin_dir" setting to override this behaviour.

This adds some complexity to the bot but unifies the project.
It might make sense to remove the "plugin_dir" setting.
2020-10-28 17:17:34 +01:00
b68e5ceedc README.adoc: fix GPL notice 2020-10-28 13:11:46 +01:00
0d0d0b6863 CMakeLists.txt: omit end{if,foreach} expressions
Their usefulness was almost negative.
2020-10-28 11:30:37 +01:00
577fd1b446 CMakeLists.txt: clean up and fix build rules
The multiple-output custom command ran separately for each binary.
2020-10-28 11:23:39 +01:00
500c83231f Bump minimum CMake version to 3.0
A nice, round number.
2020-10-27 12:02:47 +01:00
4b7649211a Bump copyright years 2020-10-27 12:02:46 +01:00
9afa4944b6 Bump liberty
Testing in production is discouraged.
2020-10-24 19:10:55 +02:00
e58ce1f02e Bump liberty, test UTF-8 sanitization 2020-10-21 05:44:27 +02:00
98e95de90e degesch: add a hidden LOMEM compile option 2020-10-20 02:02:09 +02:00
383f6af344 Improve OpenSSL integration
Ensure the error stack is cleared after errors are processed.

Also handle NULL returns safely.

Makes the debug mode spew more data, though almost none of
the contexts is in reaction to network peer data.
2020-10-20 01:55:46 +02:00
13c85aa361 degesch: comment about improving word wrapper 2020-10-19 23:37:19 +02:00
419b02e9f7 degesch: slightly cut down memory usage
The worst offenders are actually OpenSSL and Lua, this is
mostly about a preventable surprise.

This is more correct because we mix escape sequences for
attributes with text, however in practice no one will use
shit-jizz with degesch.

It is also a clean-up: "struct line_char" has been almost
halved in size.  We used to use it as a cache and now we
recompute the multibyte sequence.

Of course, it'd be best to get rid of the linked list but
it would take a very long time to rewrite the algorithm.
Plus, it's not certain that it could be improved by much.

The change in "struct line_char_attrs" is merely cosmetical.
2020-10-19 06:38:31 +02:00
c89032e4e0 degesch: silence the compiler 2020-10-19 05:17:41 +02:00
474657c7b3 degesch: fix processing WHO replies
We don't want to print the reply for ourselves
nor for unknown or PM-only users.
2020-10-19 04:21:52 +02:00
323a372389 degesch: update an outdated comment 2020-10-16 23:29:05 +02:00
76f4e6faa6 degesch: cleanup
Channels now need a reference to the server,
so don't pass it to functions.
2020-10-16 21:17:57 +02:00
2c48bc9959 degesch: watch away statuses with away-notify/WHO
We're not going to implement polling.  Polling is complex.
Freenode supports away-notify.
2020-10-16 21:17:57 +02:00
e1a4fab40d degesch: don't eat NAMES for unknown channels 2020-10-16 17:59:51 +02:00
1ff80ddd10 degesch: stubplement TAGMSG 2020-10-16 17:59:50 +02:00
12c8ace6a1 degesch: clarify handling of unexpected JOINs
I got confused about safety.
2020-10-16 17:59:50 +02:00
49706efe86 degesch: improve a function name
3_3_3_3_4 looks awful and it wasn't even precise.
2020-10-16 17:59:43 +02:00
9d8a7a10d0 Tolerate cut-off UTF-8 messages
I've had this happen to me on Russian channels and it's highly
annoying because you lose the entire message.  On the contrary,
this at worst screws up the last few characters of it.

Closes #2
2020-10-12 23:45:27 +02:00
73c3ca3633 Bump liberty 2020-10-12 23:00:43 +02:00
559232ccb5 kike: fix up debug messages 2020-10-12 04:33:39 +02:00
6837fdb7c4 Bump liberty
We've moved most of our configuration test in there.
2020-10-12 04:08:09 +02:00
2759c311fa kike: use read/write rather than recv/send
read/write support non-sockets, otherwise they're the same here.

This is in preparation for fuzzing.
2020-10-12 04:04:06 +02:00
529a46ad41 degesch: add support for crossed-out text
Assuming that sgr0 includes rmxx behaviour, which should be true.
2020-10-11 18:07:26 +02:00
f9ef123171 degesch: support more colours 2020-10-11 17:49:31 +02:00
f51dd936f5 degesch: prefer British spelling in comments
Let's say the rest is in Oxford spelling, not sure about it.
2020-10-11 17:48:57 +02:00
7ce1615021 prime.lua: skip colour sequences, add config
Colour sequence skipping is somewhat involved, we might want to
add a helper generator to the "degesch" Lua library, in the form of
{substring, is_formatting}.

formatter_parse_mirc() isn't useful, a pure Lua implementation
would be more appropriate (where do we put that?)
2020-10-11 16:54:15 +02:00
270d9017e9 degesch: improve ad-hoc IRC parsers in plugins 2020-10-10 17:58:33 +02:00
ee5cac4f21 degesch: add a plugin to highlight prime numbers 2020-10-10 17:55:14 +02:00
59ac02d91f Bump liberty
resolve_relative_runtime_unique_filename() used to have a bug.
2020-10-10 04:37:08 +02:00
d78cf10f04 degesch: fix prompt not showing up after change
When a backlog helper was running and the prompt changed,
it failed to restore within input_rl_show().

Since before input_rl_show() is called the prompt is empty
and in input_rl__restore() it will be changed to the new
version, just skip invoking any Readline functions within
input_rl_set_prompt() when the prompt is hidden.  Simple
and straight-forward.

This bug is what I hinted at in the previous commit.
2020-10-06 13:42:27 +02:00
572a7cb804 README.adoc: update degesch instructions
There is still one outstanding issue with the backlog helper, though...
2020-10-04 12:27:17 +02:00
03e8ad0a3e degesch: enable wrapping in the backlog by default
The main issue has been eliminated.
2020-10-04 12:17:09 +02:00
f665f147ff degesch: resolve the issue with less(1) and SO/SI
Now that I've learnt what exactly these characters are and how they
ended up in attribute strings, we can just eliminate them and disable
`backlog_helper_strip_formatting`.  Saner defaults, again.

I've also added skipping of terminfo delay sequences, so now it's less
of an issue to pipe raw attribute sequences into backlog helpers.
2020-10-04 12:04:24 +02:00
9819b75b64 degesch: make the unread marker look a bit fancier
Upstreamed after who knows how long, in a slightly modified form.
The marker looks fairly ugly without this and defaults should be
desirable.

It's possible to get the previous behaviour by resetting the separator
character in the configuration to an empty string.  It might be
a better idea in general to just disallow this value with a special
validation callback, so that there's only one way to do it.

However given that without fancy-prompt.lua, an optional plugin,
the long line stands out considerably, it might actually be a good
idea to keep the old behaviour as the default.  I'm torn.

Right now we don't care about the situation where the string occupies
more than one terminal cell or is some Unicode BS.  User's problem.
2020-10-04 10:08:30 +02:00
f716e7601f degesch: fix a typo 2020-10-04 08:44:16 +02:00
eea761d9f7 degesch: make use of arguments in _new() functions 2020-10-04 08:32:15 +02:00
dd8e543a20 degesch: save some memory on channel users
`struct str` was mostly unnecessary, we can save 16+ bytes,
while performance and code readability is mostly unchanged.
2020-10-04 08:28:07 +02:00
dc8b580574 degesch: expand comment about character encoding 2020-10-02 07:09:58 +02:00
2d9856cca8 Bump liberty, use iscntrl_ascii() 2020-10-02 06:52:11 +02:00
289193dd1a kike: silence an annoying build warning 2020-09-20 13:43:59 +02:00
405848deeb degesch: remove unnecessary quotes from macro defs
The behaviour is defined by the standard.
2020-09-20 13:43:36 +02:00
b9991d4766 degesch: update comment to reflect reality 2020-09-20 13:43:10 +02:00
1ff82ee907 Update NEWS, bump version 2020-09-02 20:00:12 +02:00
57e92fbb85 Update copyright years 2020-09-02 20:00:11 +02:00
a04dfc59fe README: improve libasciidoc compatibility 2020-09-02 20:00:11 +02:00
7f69655c54 README: discourage from using libedit 2020-09-02 20:00:10 +02:00
444f97b357 degesch: work around a libedit attribute issue 2020-09-02 20:00:10 +02:00
ed7130a664 degesch: fix a libedit crash 2020-09-02 20:00:10 +02:00
ba1c2357af degesch: fix Lua 5.4 build
Not sure about how well it works yet.

Lua 5.3 is still made preferential by the order of pkgconfig lookup.
2020-09-02 20:00:09 +02:00
a48023553e degesch: fix a pointer operation in the libedit layer 2020-09-02 20:00:09 +02:00
d29317b29c Bump liberty 2020-09-02 20:00:09 +02:00
deb096a0e9 Name change 2020-09-02 19:37:29 +02:00
722fc48a30 CMakeLists.txt: add a comment 2020-09-02 19:37:26 +02:00
6287e20919 degesch: fix log reopening after a buffer rename 2020-03-23 00:41:08 +01:00
07d59db5ab degesch: clean up unused functions 2020-03-22 02:00:57 +01:00
2909b017fb Fix handling terminal resizes while the terminal is suspended
GNU Readline has a misfeature.
2020-03-21 22:02:02 +01:00
64d4009427 degesch: fix getpwuid usage
The "entry not found" case doesn't have to touch errno.
2019-12-07 21:18:20 +01:00
4179a9bd49 Update NEWS, bump version 2018-10-21 05:44:39 +02:00
aa4e86c2a0 degesch: add a comment about ENOTCONN 2018-10-21 05:40:24 +02:00
5bbe9ceef8 Update NEWS 2018-10-21 05:40:24 +02:00
f80226620c kike: fix wildcard handling in WHOIS 2018-10-21 05:40:24 +02:00
2fccfb10f7 kike: allow STATS with no parameters
We were in plain conflict with RFC 2812 for no apparent reason.
2018-10-21 05:40:16 +02:00
b9eddabedd kike: explicit conversion from pointer to boolean
In practice the values in the map may only be 1 or 0, so it doesn't
matter, but in C it is better to be safe than sorry.
2018-08-01 09:22:59 +02:00
50ed74a740 kike: break out properly on errors in MODE processing
We used to only abort the inner loop, which was insufficient.
2018-08-01 09:21:37 +02:00
3ca08badc2 kike: reset user modes while processing USER
Since the processing always succeeds and registration cannot be undone,
this doesn't seem to fix any real issue.
2018-08-01 09:17:45 +02:00
b0f5b8c10d kike: do nothing on equivalent renicks 2018-08-01 09:17:12 +02:00
d87d533078 kike: code cleanups 2018-08-01 09:16:45 +02:00
3c47e5b354 kike: fix grammar in hostname validation
This has an entry in RFC 2812 errata, although it's held for document
update.  We can afford the strictness.
2018-08-01 09:16:45 +02:00
54d3406175 kike: fix grammar in config item description 2018-08-01 09:16:44 +02:00
f79dd027e9 kike: add a comment about identifier encoding 2018-08-01 09:16:44 +02:00
fa78831cbd Update NEWS, bump version 2018-06-22 00:59:41 +02:00
94b0ec80cf fancy-prompt.lua: workaround a Readline UTF-8 bug 2018-06-22 00:55:17 +02:00
300f9a9708 Bump liberty 2018-06-22 00:03:21 +02:00
b1a89f313a degesch: add static analysis for the logger
Caught two more occurences than I was able to find by just scanning
the source, so the effort wasn't in vain.
2018-06-21 23:46:03 +02:00
fab5115cd0 Remove .travis.yml
We don't depend on any proprietary services no longer.  I'll have to
make my own replacements with blackjack and hookers.  Until then,
the file stays in the commit log as an example.
2018-06-21 23:46:03 +02:00
d0cb3c1ac6 Update README 2018-06-21 23:46:03 +02:00
a0e9ede3e3 Relicense to 0BSD, update mail address
I've come to the conclusion that copyright mostly just stands in the way
of software development.  In my jurisdiction I cannot give up my own
copyright and 0BSD seems to be the closest thing to public domain.

The updated mail address, also used in my author/committer lines,
is shorter and looks nicer.  People rarely interact anyway.
2018-06-21 23:46:03 +02:00
787569e653 Update submodule URL for liberty 2018-06-21 23:45:55 +02:00
5d353b0721 Prepare NEWS for the next release 2018-06-21 23:26:16 +02:00
006d34eeae degesch: fix some log messages
We could use some static analysis for these.
2018-06-21 23:25:45 +02:00
19400ee8b7 kike: disable TLS session reuse 2018-01-09 06:25:16 +01:00
674ffb2f6d kike: handle accept() errors better
Might prevent some denial of service attacks.
2018-01-09 05:48:36 +01:00
6c30452b28 kike: thorough review, no functional changes 2018-01-09 05:47:37 +01:00
670e1c5770 kike: introduce cstr_set() 2018-01-08 23:16:14 +01:00
4586b0e1e4 degesch: introduce cstr_set() 2018-01-08 22:19:28 +01:00
b4507b56af degesch: thorough review, no functional changes 2018-01-08 22:19:23 +01:00
bf6d507bb2 degesch: fix IPv6:port in irc_split_host_port() 2018-01-08 22:19:02 +01:00
099a49e6d5 degesch: fix a minor bug in buffer_merge()
The pointer to the last item in the linked list wasn't always fixed,
although nothing really touched it afterwards.
2018-01-08 22:17:02 +01:00
4627ee82dd degesch: simplify a popular assertion 2018-01-08 22:16:57 +01:00
682f90e989 degesch: simplify the configuration dumper 2018-01-08 22:16:52 +01:00
277af83100 degesch: show an error message on log write failure
Running out of space and I/O errors seem like the most likely causes.
2018-01-08 22:16:36 +01:00
a5a0078def degesch: make buffer index computation easier to follow 2018-01-08 22:16:10 +01:00
868e34d15c degesch: fix a Lua error message 2018-01-08 22:16:06 +01:00
dc47b16034 Bump liberty, avoid fall-through warnings 2018-01-07 17:37:19 +01:00
d0f19f8be3 Update README
"Edgy" is actually a fitting word.  And we've lost OBS!
2017-12-06 23:49:18 +01:00
ddb45a1cc4 Update README 2017-12-02 13:06:39 +01:00
3974919741 Update README
So be it, SJWHub, at least I have a reason to move.
2017-12-02 11:06:48 +01:00
36be830bfc degesch: better shift state encoding handling
I don't know, probably didn't matter.
2017-07-07 20:55:25 +02:00
f7dce5e861 slack.lua: add a feature to undo emoji 2017-07-03 06:45:46 +02:00
757047bd20 CMakeLists.txt: fix variable name 2017-07-03 06:44:15 +02:00
a2611cdc3c Rework constructors/destructors 2017-06-22 22:56:24 +02:00
68bc297809 Bump liberty 2017-06-22 22:39:39 +02:00
933760c2a2 kike: fix two memory leaks 2017-06-22 20:36:21 +02:00
156ea32a90 slack.lua: support @here and @channel 2017-05-18 10:44:36 +02:00
f744681b17 slack.lua: improve input hook matching 2017-05-17 15:05:51 +02:00
bdc6334aec slack.lua: more unfucking
And now it's already fairly usable.
2017-05-17 00:32:54 +02:00
96864517c6 Fix licensing notice in README 2017-05-14 22:13:00 +02:00
0bdcd4aa8b fancy-prompt.lua: remove unnecessary local variable 2017-05-13 20:04:21 +02:00
b18a8048c1 degesch: add a slack plugin
Slack's IRC gateway is crap but it doesn't need to be *such* crap.
2017-05-13 20:04:21 +02:00
c3d62b8799 Avoid the "poller_fd::closed" feature
Reliability enhancement for Linux.

This feature was created for ponymap, however we don't care about an
extra syscall in most places.  Doing it right even saves lines.
2017-05-06 21:35:44 +02:00
ec842db0fb Update copyright years 2017-04-22 19:41:27 +02:00
0981df485a degesch: simplify quitting
- send a QUIT on C-c, too
 - shut down the connection on /disconnect, too

Connection management is one of the few fucked up parts
that remain in that state for historical reasons.
2017-04-20 20:55:49 +02:00
9f0c18cc41 degesch: fix confusing message
It seemed like we were connecting albeit we were connected already.
2017-04-20 20:26:04 +02:00
1313a712df degesch: make a second /disconnect always succeed 2017-04-20 20:25:21 +02:00
f45f9ab873 Travis CI: brevify notifications 2017-02-03 23:17:15 +01:00
9e5725662f Bump liberty 2017-01-23 23:50:27 +01:00
0785a6f417 degesch: Lua is no longer experimental
But rather essential to me.
2017-01-23 23:41:14 +01:00
cb9957cd64 Travis CI: try adding the PPA back 2016-12-30 14:47:50 +01:00
40bb2497f7 Travis CI: try removing a dead PPA 2016-12-30 14:42:06 +01:00
d7960b463f Fix LibreSSL compatibility 2016-12-30 08:51:49 +01:00
3c048f0d56 Bump version 2016-12-30 08:15:44 +01:00
8e668ff31a Various fixes related to channel modes
Bugs unnoticed for so long.
2016-12-30 08:08:34 +01:00
eb70bf3fbc Cleanup 2016-12-28 12:44:27 +01:00
d86a68f510 Add support for OpenSSL 1.1.0 2016-12-28 12:40:47 +01:00
d6be22291d degesch: /query w/o arguments just opens the query 2016-12-06 13:51:16 +01:00
a813babb89 fancy-prompt.lua: fix parametrized modes 2016-12-02 12:28:55 +01:00
b666ce6926 fancy-prompt.lua: change background on highlight 2016-12-02 12:28:55 +01:00
e2bb051bd3 degesch: replace degesch.connect with async.dial
Halfway there, looks much saner.
2016-11-04 22:02:26 +01:00
52d1ded7df degesch: move the Lua async code within the file 2016-11-04 20:44:23 +01:00
cb9f187f80 degesch: get rid of Lua timer hooks
Since they were the exception and have been replaced with the async API.
2016-11-04 20:21:46 +01:00
0247c4667a degesch: Lua coroutine safety 2016-11-04 20:12:28 +01:00
572f4e2ea3 degesch: implement Lua coroutine async basics 2016-11-04 20:11:59 +01:00
50599e09bd Update README, add a screenshot for degesch 2016-10-30 18:52:20 +01:00
b24bb0aded degesch: fix join/part hiding in the backlog 2016-10-30 16:24:23 +01:00
7c6cf42075 thin-cursor.lua: update comments 2016-10-30 01:50:21 +02:00
414a525c4d degesch: add a thin-cursor plugin 2016-10-30 00:00:48 +02:00
6cee7159f2 degesch: clean up
Caught by Coverity, however it is quite harmless.
2016-10-29 21:08:15 +02:00
568f9b7123 degesch: tiny fixes for the prompt hook
It should return valid UTF-8.

Also remember to refresh the prompt upon hook removal.
2016-10-29 20:03:31 +02:00
0d499dd125 degesch: avoid senseless indirection in hooks
It's always been one function call only this far.
2016-10-29 19:51:54 +02:00
37e49b54cf degesch: rename things around terminal attributes 2016-10-29 18:07:28 +02:00
742d590b8d degesch: simplify "attribute_printer"
Now that the line wrapper took over some of the state.
2016-10-29 17:53:06 +02:00
b6528c73e3 degesch: microoptimization 2016-10-28 18:16:21 +02:00
1e79aaec26 degesch: refresh the prompt when a hook is set 2016-10-28 13:58:37 +02:00
0995da3900 degesch: don't consider all mode changes important 2016-10-28 13:32:29 +02:00
c8a826f016 degesch: optimize Lua weak refs 2016-10-28 13:09:50 +02:00
95c7ababc3 degesch: add a "fancy-prompt" plugin
So that the client looks at least a tiny bit decent if needed.
2016-10-28 12:53:18 +02:00
a0d733fdb9 Update NEWS, README 2016-10-28 12:47:11 +02:00
557a39c6c8 degesch: export server state as a string to Lua 2016-10-28 12:47:11 +02:00
745e758394 degesch: add Lua API for screen size retrieval 2016-10-28 04:12:06 +02:00
b60bdf119a degesch: add a prompt hook 2016-10-28 04:12:06 +02:00
278e2b236b degesch: add introspection for refs within str_maps
This required some fixes to the design.
2016-10-28 04:12:05 +02:00
2f758bbdb9 degesch: allow lists of refs in introspection 2016-10-28 04:12:05 +02:00
911276b263 degesch: add introspection for "app_context" 2016-10-28 04:12:05 +02:00
cb5ad675a6 degesch: add introspection for "str" and "str_map" 2016-10-28 04:12:05 +02:00
9408dfc67c degesch: create Lua refs through introspection 2016-10-28 04:12:05 +02:00
fed8b06aff degesch: begin work on direct introspection 2016-10-28 04:12:05 +02:00
7e64fd9886 degesch: cleanup 2016-10-28 04:12:05 +02:00
6928184a3d degesch: defer prompt refreshing
Now that we do it each time we receive a message from the server.
2016-10-23 17:34:52 +02:00
f7155f3919 degesch: allow hiding join/part messages 2016-10-23 17:14:24 +02:00
f032466307 degesch: comments, no functional change 2016-10-23 17:14:24 +02:00
c0f4b554ef degesch: show channel user count in the status 2016-10-23 17:14:24 +02:00
639da7a9a7 degesch: accept Word shortcuts for formatting
Because why not.
2016-10-23 13:40:04 +02:00
230b04014f Bump liberty, add consts to some arguments 2016-10-23 13:38:46 +02:00
4848354bb9 Get rid of the remaining FAILs 2016-10-11 12:05:17 +02:00
8028c7fa47 Bump liberty 2016-10-11 10:52:49 +02:00
43de836b91 degesch: exit with error when arguments are given 2016-09-29 13:40:15 +02:00
16d10f574b degesch: simplify highlight detection 2016-09-25 14:11:30 +02:00
4cefa5ab1b degesch: fix highlight detection in colored text 2016-09-23 23:46:26 +02:00
92a4d4b5a7 Better support for the KILL command 2016-09-23 22:50:30 +02:00
26f94d2459 degesch: add a "censor" plugin
So far this approach screws up highlights, which is actually a bug.
2016-09-23 18:59:37 +02:00
0be43691d0 Update README 2016-07-23 20:29:25 +02:00
483ab39e3c degesch: die on configuration parse errors
Seems more sensible.
2016-07-23 20:00:40 +02:00
beaf1a1f82 degesch: fix Ctrl-J in Readline 2016-07-23 19:13:55 +02:00
5613c326c9 degesch: fix CTCP handling
In `/me :\` practically no client bothers to escape the backslash but we
used to interpret it as the start of an escape sequence anyway.

Silly us, no one respects any standards.
2016-07-09 22:55:26 +02:00
41 changed files with 4579 additions and 2442 deletions

32
.clang-format Normal file
View File

@@ -0,0 +1,32 @@
# clang-format is fairly limited, and these rules are approximate:
# - array initializers can get terribly mangled with clang-format 12.0,
# - sometimes it still aligns with space characters,
# - struct name NL { NL ... NL } NL name; is unachievable.
BasedOnStyle: GNU
ColumnLimit: 80
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
BreakBeforeBraces: Allman
SpaceAfterCStyleCast: true
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignConsecutiveMacros: Consecutive
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentGotoLabels: false
# IncludeCategories has some potential, but it may also break the build.
# Note that the documentation says the value should be "Never".
SortIncludes: false
# This is a compromise, it generally works out aesthetically better.
BinPackArguments: false
# Unfortunately, this can't be told to align to column 40 or so.
SpacesBeforeTrailingComments: 2
# liberty-specific macro body wrappers.
MacroBlockBegin: "BLOCK_START"
MacroBlockEnd: "BLOCK_END"
ForEachMacros: ["LIST_FOR_EACH"]

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
/uirc3.files
/uirc3.creator*
/uirc3.includes
/uirc3.cflags
/uirc3.cxxflags

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "liberty"]
path = liberty
url = git://github.com/pjanouch/liberty.git
url = https://git.janouch.name/p/liberty.git

View File

@@ -1,44 +0,0 @@
sudo: required
dist: trusty
language: c
notifications:
irc:
channels: "irc.janouch.name#dev"
use_notice: true
skip_join: true
env:
global:
- secure: "ck6keK5tTbVCN7VGyKglS890hjovUNt2zyOydiyFtQDciaB/rvEwkKy4anMCEdZHFpGAPE9iBmNYaGUsD1Y+KifhhImVMbuThe2D8MLv5crSLRheYPbbmhO8MWPAxmQnuQhpwsUKZlHvUfX8nh+d0juNdqXklvhVml78Gi99QFw="
matrix:
- readline=ON libedit=OFF
- readline=OFF libedit=ON
addons:
coverity_scan:
project:
name: "pjanouch/uirc3"
description: "Experimental IRC client, daemon and bot"
notification_email: p.janouch@gmail.com
build_command_prepend: "cmake .. -DCMAKE_BUILD_TYPE=Release"
build_command: "make"
branch_pattern: coverity_scan
compiler:
- clang
- gcc
before_install:
# We need this PPA for a recent version of libedit
- sudo add-apt-repository ppa:ondrej/php5-5.6 -y
# We need this PPA for Lua 5.3
- sudo add-apt-repository ppa:vbernat/haproxy-1.6 -y
- sudo apt-get update -qq
install:
- sudo apt-get install -y libncursesw5-dev libreadline-dev libedit-dev
liblua5.3-dev libffi-dev help2man expect
before_script:
- mkdir build
- cd build
script:
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr
-DWANT_READLINE=$readline -DWANT_LIBEDIT=$libedit
- make all test
- cpack -G DEB
- ../test

View File

@@ -1,22 +1,28 @@
project (uirc3 C)
cmake_minimum_required (VERSION 2.8.11)
# Ubuntu 18.04 LTS and OpenBSD 6.4
cmake_minimum_required (VERSION 3.10)
project (uirc3 VERSION 1.4.0 LANGUAGES C)
# Options
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
option (WANT_LIBEDIT "Use BSD libedit for the UI" OFF)
# Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
set (CMAKE_C_STANDARD 99)
set (CMAKE_C_STANDARD_REQUIRED ON)
set (CMAKE_C_EXTENSIONS OFF)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static
set (CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Wno-unused-function")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-function")
endif ()
# Version
set (project_version "0.9.4")
set (project_version "${PROJECT_VERSION}")
# Try to append commit ID if it follows a version tag. It might be nicer if
# we could also detect dirty worktrees but that's very hard to get right.
# If we didn't need this for CPack, we could use add_custom_command to generate
# a version source/include file.
find_package (Git)
set (git_head "${PROJECT_SOURCE_DIR}/.git/HEAD")
if (GIT_FOUND AND EXISTS "${git_head}")
@@ -26,8 +32,8 @@ if (GIT_FOUND AND EXISTS "${git_head}")
set (git_ref "${PROJECT_SOURCE_DIR}/.git/${CMAKE_MATCH_1}")
if (EXISTS "${git_ref}")
configure_file ("${git_ref}" git-ref.tag COPYONLY)
endif (EXISTS "${git_ref}")
endif (git_head_content MATCHES "^ref: ([^\r\n]+)")
endif ()
endif ()
execute_process (COMMAND ${GIT_EXECUTABLE} describe --tags --match v*
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
@@ -35,8 +41,8 @@ if (GIT_FOUND AND EXISTS "${git_head}")
OUTPUT_VARIABLE git_describe OUTPUT_STRIP_TRAILING_WHITESPACE)
if (NOT git_describe_result)
string (REGEX REPLACE "^v" "" project_version "${git_describe}")
endif (NOT git_describe_result)
endif (GIT_FOUND AND EXISTS "${git_head}")
endif ()
endif ()
# Dashes make filenames confusing and upset packaging software
string (REPLACE "-" "+" project_version_safe "${project_version}")
@@ -52,21 +58,20 @@ include_directories (${libssl_INCLUDE_DIRS})
link_directories (${libssl_LIBRARY_DIRS})
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
include_directories (/usr/local/include)
link_directories (/usr/local/lib)
# Need this for SIGWINCH in FreeBSD and OpenBSD respectively;
# our POSIX version macros make it undefined
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
endif ()
# -lrt is only for glibc < 2.17
# -liconv may or may not be a part of libc
foreach (extra iconv rt)
# -lm may or may not be a part of libc
foreach (extra iconv rt m)
find_library (extra_lib_${extra} ${extra})
if (extra_lib_${extra})
list (APPEND project_libraries ${extra_lib_${extra}})
endif (extra_lib_${extra})
endforeach (extra)
endif ()
endforeach ()
include (CheckCSourceRuns)
set (CMAKE_REQUIRED_LIBRARIES ${project_libraries})
@@ -76,53 +81,53 @@ CHECK_C_SOURCE_RUNS ("#include <iconv.h>
int main () { return iconv_open (\"UTF-8//TRANSLIT\", \"ISO-8859-1\")
== (iconv_t) -1; }" ICONV_ACCEPTS_TRANSLIT)
# Dependencies for degesch
# Dependencies for xC
pkg_check_modules (libffi REQUIRED libffi)
list (APPEND degesch_libraries ${libffi_LIBRARIES})
list (APPEND xC_libraries ${libffi_LIBRARIES})
include_directories (${libffi_INCLUDE_DIRS})
link_directories (${libffi_LIBRARY_DIRS})
# FIXME: other Lua versions may be acceptable, don't know yet
pkg_search_module (lua lua53 lua5.3 lua-5.3 lua>=5.3)
option (WITH_LUA "Enable experimental support for Lua plugins" ${lua_FOUND})
# XXX: other Lua versions may be acceptable, don't know yet
pkg_search_module (lua lua53 lua5.3 lua-5.3 lua54 lua5.4 lua-5.4 lua>=5.3)
option (WITH_LUA "Enable support for Lua plugins" ${lua_FOUND})
if (WITH_LUA)
if (NOT lua_FOUND)
message (FATAL_ERROR "Lua library not found")
endif (NOT lua_FOUND)
endif ()
list (APPEND degesch_libraries ${lua_LIBRARIES})
list (APPEND xC_libraries ${lua_LIBRARIES})
include_directories (${lua_INCLUDE_DIRS})
link_directories (${lua_LIBRARY_DIRS})
endif (WITH_LUA)
endif ()
find_package (Curses)
pkg_check_modules (ncursesw ncursesw)
if (ncursesw_FOUND)
list (APPEND degesch_libraries ${ncursesw_LIBRARIES})
list (APPEND xC_libraries ${ncursesw_LIBRARIES})
include_directories (${ncursesw_INCLUDE_DIRS})
elseif (CURSES_FOUND)
list (APPEND degesch_libraries ${CURSES_LIBRARY})
list (APPEND xC_libraries ${CURSES_LIBRARY})
include_directories (${CURSES_INCLUDE_DIR})
else (CURSES_FOUND)
else ()
message (SEND_ERROR "Curses not found")
endif (ncursesw_FOUND)
endif ()
if ((WANT_READLINE AND WANT_LIBEDIT) OR (NOT WANT_READLINE AND NOT WANT_LIBEDIT))
message (SEND_ERROR "You have to choose either GNU Readline or libedit")
elseif (WANT_READLINE)
# OpenBSD's default readline is too old
if ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
include_directories (/usr/local/include/ereadline)
list (APPEND degesch_libraries ereadline)
else ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
list (APPEND degesch_libraries readline)
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
include_directories (${OPENBSD_LOCALBASE}/include/ereadline)
list (APPEND xC_libraries ereadline)
else ()
list (APPEND xC_libraries readline)
endif ()
elseif (WANT_LIBEDIT)
pkg_check_modules (libedit REQUIRED libedit)
list (APPEND degesch_libraries ${libedit_LIBRARIES})
list (APPEND xC_libraries ${libedit_LIBRARIES})
include_directories (${libedit_INCLUDE_DIRS})
endif ((WANT_READLINE AND WANT_LIBEDIT) OR (NOT WANT_READLINE AND NOT WANT_LIBEDIT))
endif ()
# Generate a configuration file
set (HAVE_READLINE "${WANT_READLINE}")
@@ -130,57 +135,41 @@ set (HAVE_EDITLINE "${WANT_LIBEDIT}")
set (HAVE_LUA "${WITH_LUA}")
include (GNUInstallDirs)
# ZyklonB is currently an odd duck but degesch follows normal XDG rules
set (zyklonb_plugin_dir ${CMAKE_INSTALL_LIBDIR}/zyklonb/plugins)
configure_file (${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
# Project source files
set (common_sources)
set (common_headers ${PROJECT_BINARY_DIR}/config.h)
add_custom_command (OUTPUT kike-replies.c kike.msg
COMMAND ${PROJECT_SOURCE_DIR}/kike-gen-replies.sh
> kike-replies.c < ${PROJECT_SOURCE_DIR}/kike-replies
DEPENDS ${PROJECT_SOURCE_DIR}/kike-replies
# Generate IRC replies--we need a custom target because of the multiple outputs
add_custom_command (OUTPUT xD-replies.c xD.msg
COMMAND ${PROJECT_SOURCE_DIR}/xD-gen-replies.sh
> xD-replies.c < ${PROJECT_SOURCE_DIR}/xD-replies
DEPENDS ${PROJECT_SOURCE_DIR}/xD-replies
COMMENT "Generating files from the list of server numerics")
set_source_files_properties (${PROJECT_BINARY_DIR}/kike-replies.c
PROPERTIES HEADER_FILE_ONLY TRUE)
add_custom_target (replies DEPENDS ${PROJECT_BINARY_DIR}/xD-replies.c)
# Build
add_executable (zyklonb zyklonb.c ${common_sources} ${common_headers})
target_link_libraries (zyklonb ${project_libraries})
add_threads (zyklonb)
foreach (name xB xC xD)
add_executable (${name} ${name}.c ${PROJECT_BINARY_DIR}/config.h)
target_link_libraries (${name} ${project_libraries})
add_threads (${name})
endforeach ()
add_executable (degesch degesch.c kike-replies.c
${common_sources} ${common_headers})
target_link_libraries (degesch ${project_libraries} ${degesch_libraries})
add_threads (degesch)
add_executable (kike kike.c kike-replies.c ${common_sources} ${common_headers})
target_link_libraries (kike ${project_libraries})
add_threads (kike)
add_dependencies (xD replies)
add_dependencies (xC replies)
target_link_libraries (xC ${xC_libraries})
# Tests
function (make_tests_for target_name)
get_target_property (sources ${target_name} SOURCES)
get_target_property (libraries ${target_name} LINK_LIBRARIES)
get_target_property (options ${target_name} COMPILE_OPTIONS)
set (test test-${target_name})
add_executable (${test} ${sources})
target_link_libraries (${test} ${libraries})
set_target_properties (${test} PROPERTIES
COMPILE_DEFINITIONS TESTING
COMPILE_OPTIONS "${options}")
add_test (NAME ${test} COMMAND ${test})
endfunction (make_tests_for)
include (CTest)
if (BUILD_TESTING)
make_tests_for (degesch)
endif (BUILD_TESTING)
add_executable (test-xC $<TARGET_PROPERTY:xC,SOURCES>)
set_target_properties (test-xC PROPERTIES COMPILE_DEFINITIONS TESTING)
target_link_libraries (test-xC $<TARGET_PROPERTY:xC,LINK_LIBRARIES>)
add_threads (test-xC)
add_dependencies (test-xC replies)
add_test (NAME test-xC COMMAND test-xC)
add_test (NAME custom-static-analysis
COMMAND ${PROJECT_SOURCE_DIR}/test-static)
endif ()
# Various clang-based diagnostics, loads of fake positives and spam
file (GLOB clang_tidy_sources *.c)
@@ -197,28 +186,31 @@ add_custom_target (clang-tidy
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
# Installation
install (TARGETS zyklonb degesch kike DESTINATION ${CMAKE_INSTALL_BINDIR})
install (TARGETS xB xC xD DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
install (DIRECTORY plugins/zyklonb/
DESTINATION ${zyklonb_plugin_dir} USE_SOURCE_PERMISSIONS)
install (DIRECTORY plugins/degesch/
DESTINATION ${CMAKE_INSTALL_DATADIR}/degesch/plugins)
# XXX: our defaults for XDG_DATA_DIRS expect /usr/local/shore or /usr/share
install (DIRECTORY plugins/xB/
DESTINATION ${CMAKE_INSTALL_DATADIR}/xB/plugins USE_SOURCE_PERMISSIONS)
install (DIRECTORY plugins/xC/
DESTINATION ${CMAKE_INSTALL_DATADIR}/xC/plugins)
# Generate documentation from program help
find_program (HELP2MAN_EXECUTABLE help2man)
if (NOT HELP2MAN_EXECUTABLE)
message (FATAL_ERROR "help2man not found")
endif (NOT HELP2MAN_EXECUTABLE)
# Generate documentation from text markup
find_program (ASCIIDOCTOR_EXECUTABLE asciidoctor)
if (NOT ASCIIDOCTOR_EXECUTABLE)
message (FATAL_ERROR "asciidoctor not found")
endif ()
foreach (page zyklonb degesch kike)
foreach (page xB xC xD)
set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
list (APPEND project_MAN_PAGES "${page_output}")
add_custom_command (OUTPUT ${page_output}
COMMAND ${HELP2MAN_EXECUTABLE} -N
"${PROJECT_BINARY_DIR}/${page}" -o ${page_output}
DEPENDS ${page}
COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
-a release-version=${project_version}
"${PROJECT_SOURCE_DIR}/${page}.adoc"
-o "${page_output}"
DEPENDS ${page}.adoc
COMMENT "Generating man page for ${page}" VERBATIM)
endforeach (page)
endforeach ()
add_custom_target (docs ALL DEPENDS ${project_MAN_PAGES})
@@ -226,13 +218,13 @@ foreach (page ${project_MAN_PAGES})
string (REGEX MATCH "\\.([0-9])$" manpage_suffix "${page}")
install (FILES "${page}"
DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}")
endforeach (page)
endforeach ()
# CPack
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Experimental IRC client, daemon and bot")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Unreasonable IRC client, daemon and bot")
set (CPACK_PACKAGE_VERSION "${project_version_safe}")
set (CPACK_PACKAGE_VENDOR "Premysl Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Janouch <p.janouch@gmail.com>")
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
set (CPACK_GENERATOR "TGZ;ZIP")

View File

@@ -1,8 +1,7 @@
Copyright (c) 2014 - 2015, Přemysl Janouch <p.janouch@gmail.com>
Copyright (c) 2014 - 2021, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF

265
NEWS
View File

@@ -1,53 +1,236 @@
1.5.0 (20xx-xx-xx) "The Show Must Go On"
* xC: made it possible to pass the cursor position to external editors,
in particular VIM and Emacs
* xC: started quoting text coming from bracketed pastes,
to minimize the risk of trying to execute filesystem paths as commands
* xC: fixed to work with post-2021-08-29 editline
* xC: extended editline's autocomplete to show all the options
1.4.0 (2021-10-06) "Call Me Scruffy Scruffington"
* xC: made message autosplitting respect text formatting
* xC: fixed displaying IRC colours above 16
* xC: offer IRCnet as an IRC network to connect to,
rather than the lunatic new Freenode
* xD: started bumping the soft limit on file descriptors to the hard one
1.3.0 (2021-08-07) "New World Order"
* xC: made nick autocompletion offer recent speakers first
* All binaries have been renamed to something even sillier,
and all references in the source tree have been redacted;
this represents a major incompatible change for all plugins;
configuration and program data have to be adjusted manually
1.2.0 (2021-07-08) "There Are Other Countries As Well"
* xC: added a /squery command for IRCnet
* xC: added trivial support for SASL EXTERNAL, enabled by adding "sasl"
to the respective server's "capabilities" list
* xC: now supporting IRCv3.2 capability negotiation, including CAP DEL
* xC: added support for IRCv3 chghost
* xC: /deop and /devoice without arguments will use the client's user
* xC: /set +=/-= now treats its argument as a string array
* xC: made "/help /command" work the same way as "/help command" does
* xC: /ban and /unban don't mangle extended bans anymore
* xC: joining new channels no longer switches to their buffer automatically
if the current input line isn't empty
* censor.lua: now stripping colours from censored messages;
their attributes are also configurable rather than always black on black
1.1.0 (2020-10-31) "What Do You Mean By 'This Isn't Germany'?"
* xC: made fancy-prompt.lua work with libedit
* xD: fixed a regression with an unspecified "bind_host"
* Miscellaneous minor improvements
1.0.0 (2020-10-29) "We're Finally There!"
* Coming with real manual pages instead of help2man-generated stubs
* xC: added support for more IRC colours and strike-through text (M-m x)
* xC: now tolerating all UTF-8 messages cut off by the server
* xC: disabled "behaviour.backlog_helper_strip_formatting" by default
since the relevant issue with ACS terminfo entries has been resolved
* xC: enabled word wrapping in the backlog by default
* xC: made the unread marker span the whole line, with a configurable
character; the previous behaviour can be obtained by setting it empty
* xC: fixed the prompt not showing back up after exiting a backlog helper
when an external event has provoked an attempt to change it
* xC: now watching fellow channel users' away status when the server
supports the away-notify capability; indicated by italicised nicknames
* xC: added a plugin to highlight prime numbers in incoming messages
* xD: make sure an unspecified "bind_host" binds to both IPv4 and IPv6
* xB: install plugins to /usr/share and look for them in XDG data dirs
* Miscellaneous little fixes
0.9.8 (2020-09-02) "Yep, Still Using It"
* xC: fixed a crash and prompt attribute output in libedit 20191231-3.1,
though users are officially discouraged from using this library
* xC: fixed Lua 5.4 build, so far the support is experimental
* Miscellaneous little fixes
0.9.7 (2018-10-21) "Business as Usual"
* xD: fix wildcard handling in WHOIS
* xD: properly handle STATS without parametetrs
* xD: abort earlier when an invalid mode character is detected while
processing channel MODE messages
* xD: do not send NICK notifications when the nickname doesn't really change
* xD: fix hostname string verification (only used for "server_name")
0.9.6 (2018-06-22) "I've Been Sitting Here All This Time"
* Code has been relicensed to 0BSD and moved to a private git hosting
* Fix LibreSSL compatibility
* xC: a second /disconnect cuts the connection by force
* xC: send a QUIT message to the IRC server on Ctrl-C
* xC: add a Slack plugin (even though the gateway's now defunct)
* xC: show an error message on log write failure
* xC: fix parsing of literal IPv6 addresses with port numbers
* xC: fix some error messages
* xC: workaround a Readline bug in the fancy-prompt.lua plugin
* xD: fix two memory leaks
* xD: improve error handling for incoming connections
* xD: disable TLS session reuse
0.9.5 (2016-12-30) "It's Time"
* Better support for the KILL command
* xC: export many more fields to the Lua API, add a prompt hook
* xC: show channel user count in the prompt
* xC: allow hiding join/part messages and other noise (Meta-Shift-H)
* xC: allow autojoining channels with keys
* xC: rejoin channels with keys on reconnect
* xC: make /query without arguments just open the buffer
* xC: add a censor plugin
* xC: die on configuration parse errors
* xC: request channel modes also on rejoin
* xC: don't show remembered channel modes on parted channels
* xC: fix highlight detection in colored text
* xC: fix CTCP handling for the real world and don't decode X-QUOTEs
* xC: add support for OpenSSL 1.1.0
0.9.4 (2016-04-28) "Oops"
* degesch: fix crash on characters invalid in Windows-1252
* xC: fix crash on characters invalid in Windows-1252
* degesch: add an auto-rejoin plugin
* xC: add an auto-rejoin plugin
* degesch: better date change messages with customizable formatting;
* xC: better date change messages with customizable formatting;
now also used in the backlog, so it looks closer to regular output
* ZyklonB: add a calc plugin providing a basic Scheme REPL
* xB: add a calc plugin providing a basic Scheme REPL
* ZyklonB: add a seen plugin
* xB: add a seen plugin
* kike, ZyklonB: use pledge(2) on OpenBSD
* xD, xB: use pledge(2) on OpenBSD
0.9.3 (2016-03-27) "Doesn't Even Suck"
* Use TLS Server Name Indication when connecting to servers
* degesch: now we erase the screen before displaying buffers
* xC: now we erase the screen before displaying buffers
* degesch: implemented word wrapping in buffers
* xC: implemented word wrapping in buffers
* degesch: added autocomplete for /topic
* xC: added autocomplete for /topic
* degesch: Lua API was improved and extended
* xC: Lua API was improved and extended
* degesch: added a basic last.fm "now playing" plugin
* xC: added a basic last.fm "now playing" plugin
* degesch: backlog limit was made configurable
* xC: backlog limit was made configurable
* degesch: allow changing the list of IRC capabilities to use if available
* xC: allow changing the list of IRC capabilities to use if available
* degesch: optimize buffer memory usage
* xC: optimize buffer memory usage
* degesch: added logging of messages sent from /quote and plugins
* xC: added logging of messages sent from /quote and plugins
* degesch: M-! and M-a to go to the next buffer in order with
a highlight or new activity respectively
* xC: M-! and M-a to go to the next buffer in order with a highlight
or new activity respectively
* degesch: added --format for previewing things like MOTD files
* xC: added --format for previewing things like MOTD files
* degesch: added /buffer goto supporting case insensitive partial matches
* xC: added /buffer goto supporting case insensitive partial matches
* kike: add support for IRCv3.2 server-time
* xD: add support for IRCv3.2 server-time
* ZyklonB: plugins now run in a dedicated data directory
* xB: plugins now run in a dedicated data directory
* ZyklonB: added a factoids plugin
* xB: added a factoids plugin
* Remote addresses are now resolved asynchronously
@@ -56,28 +239,28 @@
0.9.2 (2015-12-31)
* degesch: added rudimentary support for Lua scripting
* xC: added rudimentary support for Lua scripting
* degesch: added detection of pasting, so that it doesn't trigger other
* xC: added detection of pasting, so that it doesn't trigger other
keyboard shortcuts, such as for autocomplete
* degesch: added auto-away capability
* xC: added auto-away capability
* degesch: added an /oper command
* xC: added an /oper command
* degesch: libedit backend works again
* xC: libedit backend works again
* degesch: added capability to edit the input line using VISUAL/EDITOR
* xC: added capability to edit the input line using VISUAL/EDITOR
* degesch: added Meta-Tab to switch to the last used buffer
* xC: added Meta-Tab to switch to the last used buffer
* degesch: correctly respond to stopping and resuming (SIGTSTP)
* xC: correctly respond to stopping and resuming (SIGTSTP)
* degesch: fixed decoding of text formatting
* xC: fixed decoding of text formatting
* degesch: unseen PMs now show up as highlights
* xC: unseen PMs now show up as highlights
* degesch: various bugfixes
* xC: various bugfixes
0.9.1 (2015-09-25)
@@ -88,23 +271,23 @@
* Pulled in kqueue support
* degesch: added backlog/scrollback functionality using less(1)
* xC: added backlog/scrollback functionality using less(1)
* degesch: made showing the entire set of channel mode user prefixes optional
* xC: made showing the entire set of channel mode user prefixes optional
* degesch: nicknames in /names are now ordered
* xC: nicknames in /names are now ordered
* degesch: nicknames now use the 256-color terminal palette if available
* xC: nicknames now use the 256-color terminal palette if available
* degesch: now we skip entries in the "addresses" list that can't be resolved
* xC: now we skip entries in the "addresses" list that can't be resolved
to an address, along with displaying a more helpful message
* degesch: joins, parts, nick changes and quits don't count as new buffer
* xC: joins, parts, nick changes and quits don't count as new buffer
activity anymore
* degesch: added Meta-H to open the full log file
* xC: added Meta-H to open the full log file
* degesch: various bugfixes and little improvements
* xC: various bugfixes and little improvements
0.9.0 (2015-07-23)

View File

@@ -1,33 +1,34 @@
uirc3
=====
:compact-option:
The unethical IRC trinity. This project consists of an experimental IRC client,
daemon, and bot. It's all you're ever going to need for chatting, as long as
you can make do with minimalist software.
The unreasonable IRC trinity. This project consists of an IRC client, daemon,
and bot. It's all you're ever going to need for chatting, as long as you can
make do with minimalist software.
All of them have these potentially interesting properties:
They have these potentially interesting properties:
- full IPv6 support
- TLS support, including client certificates
- lean on dependencies (with the exception of 'degesch')
- supporting IRCv3, SOCKS, IPv6, TLS (including client certificates)
- lean on dependencies
- compact and arguably easy to hack on
- permissive license
- maximally permissive license
degesch
-------
xC
--
The IRC client. It is largely defined by being built on top of GNU Readline
that has been hacked to death. Its interface should feel somewhat familiar for
weechat or irssi users.
This is the largest application within the project. It has most of the stuff
you'd expect of an IRC client, such as being able to set up multiple servers,
a powerful configuration system, integrated help, text formatting, CTCP queries,
automatic splitting of overlong messages, autocomplete, logging to file,
auto-away, command aliases and rudimentary support for Lua scripting.
image::xC.png[align="center"]
kike
----
This is the core of the project. It has most of the stuff you'd expect of
an IRC client, such as being multiserver, a powerful configuration system,
integrated help, text formatting, automatic splitting of overlong messages,
multiline editing, bracketed paste support, decent word wrapping, autocomplete,
logging, CTCP queries, auto-away, command aliases, and basic support for Lua
scripting. As a unique bonus, you can launch a full text editor from within.
xD
--
The IRC daemon. It is designed to be used as a regular user application rather
than a system-wide daemon. If all you want is a decent, minimal IRCd for
testing purposes or a small network of respectful users (or bots), this one will
@@ -35,10 +36,8 @@ do it just fine.
Notable features:
- TLS autodetection (why doesn't everyone have this?), using secure defaults
- TLS autodetection (I'm still wondering why everyone doesn't have this)
- IRCop authentication via TLS client certificates
- epoll/kqueue support; this means that it should be able to handle quite
a number of concurrent user connections
- partial IRCv3 support
Not supported:
@@ -46,43 +45,44 @@ Not supported:
- server linking (which also means no services); I consider existing protocols
for this purpose ugly and tricky to implement correctly; I've also found no
use for this feature yet
- online changes to configuration; the configuration system from degesch could
- online changes to configuration; the configuration system from 'xC' could
be used to implement this feature if needed
- limits of almost any kind, just connections and mode `+l`
ZyklonB
-------
The IRC bot. It builds upon the concept of my other VitaminA IRC bot. The main
characteristic of these two bots is that they run plugins as coprocesses, which
allows for enhanced reliability and programming language freedom.
This program has been
https://git.janouch.name/p/haven/src/branch/master/hid[ported to Go],
and development continues over there.
While originally intended to be a simple rewrite of the original AWK bot in C,
it fairly quickly became a playground, and it eventually got me into writing
the rest of the package.
xB
--
The IRC bot. While originally intended to be a simple rewrite of my old GNU AWK
bot in C, it fairly quickly became a playground, and it eventually got me into
writing the rest of this package.
It survives crashes, server disconnects and timeouts, and also has native SOCKS
support (even though socksify can add that easily to any program).
Its main characteristic is that it runs plugins as coprocesses, allowing for
enhanced reliability and programming language freedom. Moreover, it recovers
from any crashes, and offers native SOCKS support (even though socksify can add
that easily to any program).
Packages
--------
Regular releases are sporadic. git master should be stable enough. You can get
a package with the latest development version from Archlinux's AUR, or from
openSUSE Build Service for the rest of mainstream distributions. Consult the
list of repositories and their respective links at:
https://build.opensuse.org/project/repositories/home:pjanouch:git
a package with the latest development version from Archlinux's AUR.
Building
--------
Build dependencies: CMake, pkg-config, help2man, awk, sh, liberty (included) +
Build dependencies: CMake, pkg-config, asciidoctor, awk, liberty (included) +
Runtime dependencies: openssl +
Additionally for degesch: curses, libffi, lua >= 5.3 (optional),
readline >= 6.0 or libedit >= 2013-07-12
Additionally for 'xC': curses, libffi, lua >= 5.3 (optional),
readline >= 6.0 or libedit >= 2013-07-12
$ git clone --recursive https://github.com/pjanouch/uirc3.git
Avoid libedit if you can, in general it works but at the moment history is
acting up and I have no clue about fixing it.
$ git clone --recursive https://git.janouch.name/p/uirc3.git
$ mkdir uirc3/build
$ cd uirc3/build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug \
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DWANT_READLINE=ON -DWANT_LIBEDIT=OFF -DWANT_LUA=ON
$ make
@@ -90,78 +90,105 @@ To install the application, you can do either the usual:
# make install
Or you can try telling CMake to make a package for you. For Debian it is:
Or you can try telling CMake to make a package for you:
$ cpack -G DEB
$ cpack -G DEB # also supported: RPM, FreeBSD
# dpkg -i uirc3-*.deb
Usage
-----
'degesch' has in-program configuration. Just run it and read the instructions.
'xC' has in-program configuration. Just run it and read the instructions.
Consult its link:xC.adoc[man page] for details about the interface.
For the rest you might want to generate a configuration file:
$ zyklonb --write-default-config
$ kike --write-default-config
$ xB --write-default-config
$ xD --write-default-config
After making any necessary edits to the file (there are comments to aid you in
doing that), simply run the appropriate program with no arguments:
$ zyklonb
$ kike
$ xB
$ xD
'ZyklonB' stays running in the foreground, therefore I recommend launching it
inside a Screen or tmux session.
'xB' stays running in the foreground, therefore I recommend launching it inside
a Screen or tmux session.
'kike', on the other hand, immediately forks into the background. Use the PID
'xD', on the other hand, immediately forks into the background. Use the PID
file or something like `killall` if you want to terminate it. You can run it
as a `forking` type systemd user service.
Client Certificates
-------------------
'kike' uses SHA1 fingerprints of TLS client certificates to authenticate users.
'xC' will use the SASL EXTERNAL method to authenticate using the TLS client
certificate specified by the respective server's `tls_cert` option if you add
`sasl` to the `capabilities` option and the server supports this.
'xD' uses SHA-1 fingerprints of TLS client certificates to authenticate users.
To get the fingerprint from a certificate file in the required form, use:
$ openssl x509 -in public.pem -outform DER | sha1sum
Custom Key Bindings in degesch
------------------------------
The default and preferred frontend used in 'degesch' is GNU Readline. This
means that you can change your bindings by editing '~/.inputrc'. For example:
Custom Key Bindings in xC
-------------------------
The default and preferred frontend used in 'xC' is GNU Readline. This means
that you can change your bindings by editing '~/.inputrc'. For example:
....
# Preload with system-wide settings
$include /etc/inputrc
# Make M-left and M-right reorder buffers
$if degesch
$if xC
"\e\e[C": move-buffer-right
"\e\e[D": move-buffer-left
$endif
....
Consult the source code and the GNU Readline manual for a list of available
functions. Also refer to the latter for the exact syntax of this file.
Beware that you can easily break the program if you're not careful.
How do I make xC look like the screenshot?
------------------------------------------
First of all, you must build it with Lua support. With the defaults, 'xC'
doesn't look too fancy because I don't want to depend on Lua or 256-colour
terminals. In addition to that, I appear to be one of the few people who use
black on white terminals.
/set behaviour.date_change_line = "%a %e %b %Y"
/set behaviour.plugin_autoload += "fancy-prompt.lua"
/set behaviour.backlog_helper = "LESSSECURE=1 less -R +Gb1d -Ps'Backlog ?ltlines %lt-%lb?L/%L. .?e(END):?pB%pB\\%..'"
/set attributes.userhost = "\x1b[38;5;109m"
/set attributes.join = "\x1b[38;5;108m"
/set attributes.part = "\x1b[38;5;138m"
/set attributes.external = "\x1b[38;5;248m"
/set attributes.timestamp = "\x1b[48;5;255m\x1b[38;5;250m"
/set attributes.read_marker = "\x1b[38;5;202m"
Configuration profiles
----------------------
Even though the applications don't directly support configuration profiles,
they conform to the XDG standard, and thus you can change the location they
load configuration from via XDG_CONFIG_HOME (normally '~/.config') and the
location where store their data via XDG_DATA_HOME (normally '~/.local/share').
It would be relatively easy to make the applications assume whatever name you
run them under (for example by using symbolic links), and load different
configurations accordingly, but I consider it rather messy and unnecessary.
Contributing and Support
------------------------
Use this project's GitHub to report any bugs, request features, or submit pull
requests. If you want to discuss this project, or maybe just hang out with
the developer, feel free to join me at irc://irc.janouch.name, channel #dev.
Use https://git.janouch.name/p/uirc3 to report any bugs, request features,
or submit pull requests. `git send-email` is tolerated. If you want to discuss
the project, feel free to join me at ircs://irc.janouch.name, channel #dev.
Disclaimer
----------
I am not an antisemitist, I'm just being an offensive asshole with the naming.
And no, I'm not going to change the names.
Bitcoin donations are accepted at: 12r5uEWEgcHC46xd64tt3hHt9EUvYYDHe9
License
-------
'uirc3' is written by Přemysl Janouch <p.janouch@gmail.com>.
This software is released under the terms of the 0BSD license, the text of which
is included within the package along with the list of authors.
You may use the software under the terms of the ISC license, the text of which
is included within the package, or, at your option, you may relicense the work
under the MIT or the Modified BSD License, as listed at the following site:
http://www.gnu.org/licenses/license-list.html
Note that 'degesch' technically becomes GPL-licensed when you compile it against
GNU Readline, but that is not a concern of this source package.
Note that 'xC' becomes GPL-licensed when you link it against GNU Readline,
but that is not a concern of this source package. The licenses are compatible.

224
common.c
View File

@@ -1,11 +1,10 @@
/*
* common.c: common functionality
*
* Copyright (c) 2014 - 2015, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -23,28 +22,60 @@
#define LIBERTY_WANT_PROTO_IRC
#ifdef WANT_SYSLOG_LOGGING
#define print_fatal_data ((void *) LOG_ERR)
#define print_error_data ((void *) LOG_ERR)
#define print_warning_data ((void *) LOG_WARNING)
#define print_status_data ((void *) LOG_INFO)
#define print_debug_data ((void *) LOG_DEBUG)
#define print_fatal_data ((void *) LOG_ERR)
#define print_error_data ((void *) LOG_ERR)
#define print_warning_data ((void *) LOG_WARNING)
#define print_status_data ((void *) LOG_INFO)
#define print_debug_data ((void *) LOG_DEBUG)
#endif // WANT_SYSLOG_LOGGING
#include "liberty/liberty.c"
#include <arpa/inet.h>
#include <netinet/tcp.h>
/// Shorthand to set an error and return failure from the function
#define FAIL(...) \
BLOCK_START \
error_set (e, __VA_ARGS__); \
return 0; \
BLOCK_END
static void
init_openssl (void)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER
SSL_library_init ();
// XXX: this list is probably not complete
atexit (EVP_cleanup);
SSL_load_error_strings ();
atexit (ERR_free_strings);
#else
// Cleanup is done automatically via atexit()
OPENSSL_init_ssl (0, NULL);
#endif
}
// --- To be moved to liberty --------------------------------------------------
// FIXME: in xssl_get_error() we rely on error reasons never being NULL (i.e.,
// all loaded), which isn't very robust.
// TODO: check all places where this is used and see if we couldn't gain better
// information by piecing together some other subset of data from the error
// stack. Most often, this is used in an error_set() context, which would
// allow us to allocate memory instead of returning static strings.
static const char *
xerr_describe_error (void)
{
unsigned long err = ERR_get_error ();
if (!err)
return "undefined error";
const char *reason = ERR_reason_error_string (err);
do
// Not thread-safe, not a concern right now--need a buffer
print_debug ("%s", ERR_error_string (err, NULL));
while ((err = ERR_get_error ()));
if (!reason)
return "cannot retrieve error description";
return reason;
}
static ssize_t
str_vector_find (const struct str_vector *v, const char *s)
strv_find (const struct strv *v, const char *s)
{
for (size_t i = 0; i < v->len; i++)
if (!strcmp (v->vector[i], s))
@@ -56,129 +87,15 @@ static time_t
unixtime_msec (long *msec)
{
#ifdef _POSIX_TIMERS
struct timespec tp;
hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
*msec = tp.tv_nsec / 1000000;
struct timespec tp;
hard_assert (clock_gettime (CLOCK_REALTIME, &tp) != -1);
*msec = tp.tv_nsec / 1000000;
#else // ! _POSIX_TIMERS
struct timeval tp;
hard_assert (gettimeofday (&tp, NULL) != -1);
*msec = tp.tv_usec / 1000;
struct timeval tp;
hard_assert (gettimeofday (&tp, NULL) != -1);
*msec = tp.tv_usec / 1000;
#endif // ! _POSIX_TIMERS
return tp.tv_sec;
}
/// This differs from the non-unique version in that we expect the filename
/// to be something like a pattern for mkstemp(), so the resulting path can
/// reside in a system-wide directory with no risk of a conflict.
static char *
resolve_relative_runtime_unique_filename (const char *filename)
{
struct str path;
str_init (&path);
const char *runtime_dir = getenv ("XDG_RUNTIME_DIR");
const char *tmpdir = getenv ("TMPDIR");
if (runtime_dir && *runtime_dir == '/')
str_append (&path, runtime_dir);
else if (tmpdir && *tmpdir == '/')
str_append (&path, tmpdir);
else
str_append (&path, "/tmp");
str_append_printf (&path, "/%s/%s", PROGRAM_NAME, filename);
// Try to create the file's ancestors;
// typically the user will want to immediately create a file in there
const char *last_slash = strrchr (path.str, '/');
if (last_slash && last_slash != path.str)
{
char *copy = xstrndup (path.str, last_slash - path.str);
(void) mkdir_with_parents (copy, NULL);
free (copy);
}
return str_steal (&path);
}
static bool
xwrite (int fd, const char *data, size_t len, struct error **e)
{
size_t written = 0;
while (written < len)
{
ssize_t res = write (fd, data + written, len - written);
if (res >= 0)
written += res;
else if (errno != EINTR)
FAIL ("%s", strerror (errno));
}
return true;
}
// --- Simple network I/O ------------------------------------------------------
// TODO: move to liberty and remove from dwmstatus.c as well
#define SOCKET_IO_OVERFLOW (8 << 20) ///< How large a read buffer can be
enum socket_io_result
{
SOCKET_IO_OK, ///< Completed successfully
SOCKET_IO_EOF, ///< Connection shut down by peer
SOCKET_IO_ERROR ///< Connection error
};
static enum socket_io_result
socket_io_try_read (int socket_fd, struct str *rb, struct error **e)
{
// We allow buffering of a fair amount of data, however within reason,
// so that it's not so easy to flood us and cause an allocation failure
ssize_t n_read;
while (rb->len < SOCKET_IO_OVERFLOW)
{
str_ensure_space (rb, 4096);
n_read = recv (socket_fd, rb->str + rb->len,
rb->alloc - rb->len - 1 /* null byte */, 0);
if (n_read > 0)
{
rb->str[rb->len += n_read] = '\0';
continue;
}
if (n_read == 0)
return SOCKET_IO_EOF;
if (errno == EAGAIN)
return SOCKET_IO_OK;
if (errno == EINTR)
continue;
error_set (e, "%s", strerror (errno));
return SOCKET_IO_ERROR;
}
return SOCKET_IO_OK;
}
static enum socket_io_result
socket_io_try_write (int socket_fd, struct str *wb, struct error **e)
{
ssize_t n_written;
while (wb->len)
{
n_written = send (socket_fd, wb->str, wb->len, 0);
if (n_written >= 0)
{
str_remove_slice (wb, 0, n_written);
continue;
}
if (errno == EAGAIN)
return SOCKET_IO_OK;
if (errno == EINTR)
continue;
error_set (e, "%s", strerror (errno));
return SOCKET_IO_ERROR;
}
return SOCKET_IO_OK;
return tp.tv_sec;
}
// --- Logging -----------------------------------------------------------------
@@ -762,7 +679,7 @@ socks_try_fill_read_buffer (struct socks_connector *self, size_t n)
return true;
ssize_t received;
str_ensure_space (&self->read_buffer, remains);
str_reserve (&self->read_buffer, remains);
do
received = recv (self->socket_fd,
self->read_buffer.str + self->read_buffer.len, remains, 0);
@@ -786,8 +703,8 @@ socks_call_on_data (struct socks_connector *self)
if (self->read_buffer.len < to_consume)
return true;
struct msg_unpacker unpacker;
msg_unpacker_init (&unpacker, self->read_buffer.str, self->read_buffer.len);
struct msg_unpacker unpacker =
msg_unpacker_make (self->read_buffer.str, self->read_buffer.len);
bool result = self->on_data (self, &unpacker);
str_remove_slice (&self->read_buffer, 0, to_consume);
return result;
@@ -852,16 +769,16 @@ socks_connector_init (struct socks_connector *self, struct poller *poller)
{
memset (self, 0, sizeof *self);
poller_fd_init (&self->socket_event, poller, (self->socket_fd = -1));
self->socket_event = poller_fd_make (poller, (self->socket_fd = -1));
self->socket_event.dispatcher = (poller_fd_fn) socks_connector_on_ready;
self->socket_event.user_data = self;
poller_timer_init (&self->timeout, poller);
self->timeout = poller_timer_make (poller);
self->timeout.dispatcher = (poller_timer_fn) socks_connector_on_timeout;
self->timeout.user_data = self;
str_init (&self->read_buffer);
str_init (&self->write_buffer);
self->read_buffer = str_make ();
self->write_buffer = str_make ();
}
static void
@@ -962,8 +879,8 @@ static struct ctcp_chunk *
ctcp_chunk_new (void)
{
struct ctcp_chunk *self = xcalloc (1, sizeof *self);
str_init (&self->tag);
str_init (&self->text);
self->tag = str_make ();
self->text = str_make ();
return self;
}
@@ -1023,6 +940,13 @@ ctcp_intra_decode (const char *chunk, size_t len, struct str *output)
}
}
// According to the original CTCP specification we should use
// ctcp_intra_decode() on all parts, however no one seems to use that
// and it breaks normal text with backslashes
#ifndef SUPPORT_CTCP_X_QUOTES
#define ctcp_intra_decode(s, len, output) str_append_data (output, s, len)
#endif
static void
ctcp_parse_tagged (const char *chunk, size_t len, struct ctcp_chunk *output)
{
@@ -1045,15 +969,11 @@ ctcp_parse_tagged (const char *chunk, size_t len, struct ctcp_chunk *output)
static struct ctcp_chunk *
ctcp_parse (const char *message)
{
struct str m;
str_init (&m);
struct str m = str_make ();
ctcp_low_level_decode (message, &m);
struct ctcp_chunk *result = NULL, *result_tail = NULL;
// According to the original CTCP specification we should use
// ctcp_intra_decode() on all parts, however no one seems to
// use that and it breaks normal text with backslashes
size_t start = 0;
bool in_ctcp = false;
for (size_t i = 0; i < m.len; i++)
@@ -1077,7 +997,7 @@ ctcp_parse (const char *message)
if (my_is_ctcp)
ctcp_parse_tagged (m.str + my_start, i - my_start, chunk);
else
str_append_data (&chunk->text, m.str + my_start, i - my_start);
ctcp_intra_decode (m.str + my_start, i - my_start, &chunk->text);
LIST_APPEND_WITH_TAIL (result, result_tail, chunk);
}
@@ -1091,7 +1011,7 @@ ctcp_parse (const char *message)
chunk->is_partial = true;
}
else
str_append_data (&chunk->text, m.str + start, m.len - start);
ctcp_intra_decode (m.str + start, m.len - start, &chunk->text);
LIST_APPEND_WITH_TAIL (result, result_tail, chunk);
}

View File

@@ -2,7 +2,6 @@
#define CONFIG_H
#define PROGRAM_VERSION "${project_version}"
#define ZYKLONB_PLUGIN_DIR "${CMAKE_INSTALL_PREFIX}/${zyklonb_plugin_dir}"
#cmakedefine HAVE_READLINE
#cmakedefine HAVE_EDITLINE

Submodule liberty updated: 365aed456e...1b9d89cab3

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env guile
ZyklonB calc plugin, basic Scheme evaluator
xB calc plugin, basic Scheme evaluator
Copyright 2016 Přemysl Janouch
Copyright 2016 Přemysl Eric Janouch
See the file LICENSE for licensing information.
!#
@@ -77,7 +77,7 @@
(substring line 0 (- len 1)) line))))
(define (get-config name)
(send "ZYKLONB get_config :" name)
(send "XB get_config :" name)
(car (message-params (parse-message (get-line-crlf irc-input-port)))))
(define (extract-nick prefix)
@@ -216,7 +216,7 @@
; --- Main loop ----------------------------------------------------------------
(define prefix (get-config "prefix"))
(send "ZYKLONB register")
(send "XB register")
(define (process msg)
(when (string-ci=? (message-command msg) "PRIVMSG")

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env tclsh
#
# ZyklonB coin plugin, random number-based utilities
# xB coin plugin, random number-based utilities
#
# Copyright 2012, 2014 Přemysl Janouch
# Copyright 2012, 2014 Přemysl Eric Janouch
# See the file LICENSE for licensing information.
#
@@ -37,7 +37,7 @@ proc parse {line} {
proc get_config {key} {
global msg
puts "ZYKLONB get_config :$key"
puts "XB get_config :$key"
gets stdin line
parse $line
return [lindex $msg(param) 0]
@@ -53,7 +53,7 @@ fconfigure stdin -translation crlf -encoding iso8859-1
fconfigure stdout -translation crlf -encoding iso8859-1
set prefix [get_config prefix]
puts "ZYKLONB register"
puts "XB register"
set eightball [list \
"It is certain" \

View File

@@ -1,8 +1,8 @@
#!/usr/bin/awk -f
#
# ZyklonB eval plugin, LISP-like expression evaluator
# xB eval plugin, LISP-like expression evaluator
#
# Copyright 2013, 2014 Přemysl Janouch
# Copyright 2013, 2014 Přemysl Eric Janouch
# See the file LICENSE for licensing information.
#
@@ -15,7 +15,7 @@ BEGIN \
prefix = get_config("prefix")
print "ZYKLONB register"
print "XB register"
fflush("")
# All functions have to be in this particular array
@@ -258,7 +258,7 @@ function process_end ()
function get_config (key)
{
print "ZYKLONB get_config :" key
print "XB get_config :" key
fflush("")
getline

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env perl
#
# ZyklonB factoids plugin
# xB factoids plugin
#
# Copyright 2016 Přemysl Janouch <p.janouch@gmail.com>
# Copyright 2016 Přemysl Eric Janouch <p@janouch.name>
# See the file LICENSE for licensing information.
#
@@ -24,18 +24,18 @@ sub parse ($) {
}
sub bot_print {
print "ZYKLONB print :${\shift}";
print "XB print :${\shift}";
}
# --- Initialization -----------------------------------------------------------
my %config;
for my $name (qw(prefix)) {
print "ZYKLONB get_config :$name";
print "XB get_config :$name";
$config{$name} = (parse <STDIN>)->{args}->[0];
}
print "ZYKLONB register";
print "XB register";
# --- Database -----------------------------------------------------------------
# Simple map of (factoid_name => [definitions]); all factoids are separated

View File

@@ -1,9 +1,9 @@
#!/usr/bin/env ruby
# coding: utf-8
#
# ZyklonB pomodoro plugin
# xB pomodoro plugin
#
# Copyright 2015 Přemysl Janouch
# Copyright 2015 Přemysl Eric Janouch
# See the file LICENSE for licensing information.
#
@@ -206,7 +206,7 @@ def parse (line)
end
def bot_print (what)
print "ZYKLONB print :#{what}"
print "XB print :#{what}"
end
# --- Initialization -----------------------------------------------------------
@@ -215,12 +215,12 @@ end
# To read it from anywhere else, it has to be done asynchronously
$config = {}
[:prefix].each do |name|
print "ZYKLONB get_config :#{name}"
print "XB get_config :#{name}"
_, _, _, _, args = *parse($stdin.gets.chomp)
$config[name] = args[0]
end
print "ZYKLONB register"
print "XB register"
# --- Plugin logic -------------------------------------------------------------

View File

@@ -1,8 +1,8 @@
#!/usr/bin/tcc -run -lm
//
// ZyklonB scripting plugin, using a custom stack-based language
// xB scripting plugin, using a custom stack-based language
//
// Copyright 2014 Přemysl Janouch
// Copyright 2014 Přemysl Eric Janouch
// See the file LICENSE for licensing information.
//
// Just compile this file as usual (sans #!) if you don't feel like using TCC.
@@ -1964,12 +1964,12 @@ read_message (void)
// --- Interfacing with the bot ------------------------------------------------
#define BOT_PRINT "ZYKLONB print :script: "
#define BOT_PRINT "XB print :script: "
static const char *
get_config (const char *key)
{
printf ("ZYKLONB get_config :%s\r\n", key);
printf ("XB get_config :%s\r\n", key);
struct message *msg = read_message ();
if (!msg || msg->n_params <= 0)
exit (EXIT_FAILURE);
@@ -2298,7 +2298,7 @@ main (int argc, char *argv[])
printf (BOT_PRINT "%s\r\n", "runtime library initialization failed");
g_prefix = strdup (get_config ("prefix"));
printf ("ZYKLONB register\r\n");
printf ("XB register\r\n");
struct message *msg;
while ((msg = read_message ()))
process_message (msg);

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env lua
--
-- ZyklonB seen plugin
-- xB seen plugin
--
-- Copyright 2016 Přemysl Janouch <p.janouch@gmail.com>
-- Copyright 2016 Přemysl Eric Janouch <p@janouch.name>
-- See the file LICENSE for licensing information.
--
@@ -26,7 +26,7 @@ function parse (line)
end
function get_config (name)
io.write ("ZYKLONB get_config :", name, "\r\n")
io.write ("XB get_config :", name, "\r\n")
return parse (io.read ()).params[1]
end
@@ -34,7 +34,7 @@ end
io.output ():setvbuf ('line')
local prefix = get_config ('prefix')
io.write ("ZYKLONB register\r\n")
io.write ("XB register\r\n")
local db = {}
local db_filename = "seen.db"

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env perl
# Creates a database for the "seen" plugin from logs for degesch.
# Creates a database for the "seen" plugin from logs for xC.
# The results may not be completely accurate but are good for jumpstarting.
# Usage: ./seen-import-degesch.pl LOG-FILE... > seen.db
# Usage: ./seen-import-xC.pl LOG-FILE... > seen.db
use strict;
use warnings;

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python3
#
# ZyklonB YouTube plugin, displaying info about YouTube links
# xB YouTube plugin, displaying info about YouTube links
#
# Copyright 2014 - 2015, Přemysl Janouch <p.janouch@gmail.com>
# Copyright 2014 - 2015, Přemysl Eric Janouch <p@janouch.name>
# See the file LICENSE for licensing information.
#
@@ -27,12 +27,12 @@ class Plugin:
return (nick, user, host, command, args)
def get_config (self, key):
print ("ZYKLONB get_config :%s" % key)
print ("XB get_config :%s" % key)
(_, _, _, _, args) = self.parse (sys.stdin.readline ())
return args[0]
def bot_print (self, what):
print ('ZYKLONB print :%s' % what)
print ('XB print :%s' % what)
class YouTube (Plugin):
re_videos = [re.compile (x) for x in [
@@ -98,7 +98,7 @@ class YouTube (Plugin):
if self.youtube_api_key == "":
self.bot_print ("youtube: missing `youtube_api_key'")
print ("ZYKLONB register")
print ("XB register")
for line in sys.stdin:
self.process_line (line)

View File

@@ -1,11 +1,10 @@
--
-- auto-rejoin.lua: join back automatically when someone kicks you
--
-- Copyright (c) 2016, Přemysl Janouch <p.janouch@gmail.com>
-- Copyright (c) 2016, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -17,7 +16,7 @@
--
local timeout
degesch.setup_config {
xC.setup_config {
timeout = {
type = "integer",
comment = "auto rejoin timeout",
@@ -32,16 +31,18 @@ degesch.setup_config {
},
}
degesch.hook_irc (function (hook, server, line)
local msg = degesch.parse (line)
async, await = xC.async, coroutine.yield
xC.hook_irc (function (hook, server, line)
local msg = xC.parse (line)
if msg.command ~= "KICK" then return line end
local who = msg.prefix:match ("^[^!]*")
local channel, whom = table.unpack (msg.params)
if who ~= whom and whom == server.user.nickname then
degesch.hook_timer (function (hook)
async.go (function ()
await (async.timer_ms (timeout * 1000))
server:send ("JOIN " .. channel)
end, timeout * 1000)
end)
end
return line
end)

90
plugins/xC/censor.lua Normal file
View File

@@ -0,0 +1,90 @@
--
-- censor.lua: black out certain users' messages
--
-- Copyright (c) 2016 - 2021, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
local to_pattern = function (mask)
if not mask:match ("!") then mask = mask .. "!*" end
if not mask:match ("@") then mask = mask .. "@*" end
-- That is, * acts like a wildcard, otherwise everything is escaped
return "^" .. mask:gsub ("[%^%$%(%)%%%.%[%]%+%-%?]", "%%%0")
:gsub ("%*", ".*") .. "$"
end
local patterns = {}
local read_masks = function (v)
patterns = {}
local add = function (who, where)
local channels = patterns[who] or {}
table.insert (channels, where)
patterns[who] = channels
end
for item in v:lower ():gmatch ("[^,]+") do
local who, where = item:match ("^([^/]+)/*(.*)")
if who then add (to_pattern (who), where == "" or where) end
end
end
local quote
xC.setup_config {
masks = {
type = "string_array",
default = "\"\"",
comment = "user masks (optionally \"/#channel\") to censor",
on_change = read_masks
},
quote = {
type = "string",
default = "\"\\x0301,01\"",
comment = "formatting prefix for censored messages",
on_change = function (v) quote = v end
},
}
local decolor = function (text)
local rebuilt, last = {""}, 1
for start in text:gmatch ('()\x03') do
table.insert (rebuilt, text:sub (last, start - 1))
local sub = text:sub (start + 1)
last = start + (sub:match ('^%d%d?,%d%d?()') or sub:match ('^%d?%d?()'))
end
return table.concat (rebuilt) .. text:sub (last)
end
local censor = function (line)
-- Taking a shortcut to avoid lengthy message reassembly
local start, text = line:match ("^(.- PRIVMSG .- :)(.*)$")
local ctcp, rest = text:match ("^(\x01%g+ )(.*)")
text = ctcp and ctcp .. quote .. decolor (rest) or quote .. decolor (text)
return start .. text
end
xC.hook_irc (function (hook, server, line)
local msg = xC.parse (line)
if msg.command ~= "PRIVMSG" then return line end
local channel = msg.params[1]:lower ()
for who, where in pairs (patterns) do
if msg.prefix:lower ():match (who) then
for _, x in pairs (where) do
if x == true or x == channel then
return censor (line)
end
end
end
end
return line
end)

105
plugins/xC/fancy-prompt.lua Normal file
View File

@@ -0,0 +1,105 @@
--
-- fancy-prompt.lua: the fancy multiline prompt you probably want
--
-- Copyright (c) 2016, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
-- Beware that it is a hack and only goes about 90% of the way, which is why
-- this functionality is only available as a plugin in the first place
-- (well, and also for customizability).
--
-- The biggest problem is that the way we work with Readline is incompatible
-- with multiline prompts, and normal newlines just don't work. This is being
-- circumvented by using an overflowing single-line prompt with a specially
-- crafted character in the rightmost column that prevents the bar's background
-- from spilling all over the last line.
--
-- There is also a problem with C-r search rendering not clearing out the
-- background but to really fix that mode, we'd have to fully reimplement it
-- since its alternative prompt very often gets overriden by accident anyway.
xC.hook_prompt (function (hook)
local current = xC.current_buffer
local chan = current.channel
local s = current.server
local bg_color = "255"
local current_n = 0
local active = ""
for i, buffer in ipairs (xC.buffers) do
if buffer == current then
current_n = i
elseif buffer.new_messages_count ~= buffer.new_unimportant_count then
if active ~= "" then active = active .. "," end
if buffer.highlighted then
active = active .. "!"
bg_color = "224"
end
active = active .. i
end
end
if active ~= "" then active = "(" .. active .. ")" end
local x = current_n .. ":" .. current.name
if chan and chan.users_len ~= 0 then
local params = ""
for mode, param in pairs (chan.param_modes) do
params = params .. " +" .. mode .. " " .. param
end
local modes = chan.no_param_modes .. params:sub (3)
if modes ~= "" then x = x .. "(+" .. modes .. ")" end
x = x .. "{" .. chan.users_len .. "}"
end
if current.hide_unimportant then x = x .. "<H>" end
local lines, cols = xC.get_screen_size ()
x = x .. " " .. active .. string.rep (" ", cols)
-- Readline 7.0.003 seems to be broken and completely corrupts the prompt.
-- However 8.0.004 seems to be fine with these, as is libedit 20191231-3.1.
--x = x:gsub("[\128-\255]", "?")
-- Cut off extra characters and apply formatting, including the hack.
-- FIXME: this doesn't count with full-width or zero-width characters.
-- We might want to export wcwidth() above term_from_utf8 somehow.
local overflow = utf8.offset (x, cols - 1)
if overflow then x = x:sub (1, overflow) end
x = "\x01\x1b[0;4;1;38;5;16m\x1b[48;5;" .. bg_color .. "m\x02" ..
x .. "\x01\x1b[0;4;1;7;38;5;" .. bg_color .. "m\x02 \x01\x1b[0;1m\x02"
local user_prefix = function (chan, user)
for i, chan_user in ipairs (chan.users) do
if chan_user.user == user then return chan_user.prefixes end
end
return ""
end
if s then
x = x .. "["
local state = s.state
if state == "disconnected" or state == "connecting" then
x = x .. "(" .. state .. ")"
elseif state ~= "registered" then
x = x .. "(unregistered)"
else
local user, modes = s.user, s.user_mode
if chan then x = x .. user_prefix (chan, user) end
x = x .. user.nickname
if modes ~= "" then x = x .. "(" .. modes .. ")" end
end
x = x .. "] "
else
-- There needs to be at least one character so that the cursor
-- doesn't get damaged by our hack in that last column
x = x .. "> "
end
return x
end)

View File

@@ -5,11 +5,10 @@
--
-- I call this style closure-oriented programming
--
-- Copyright (c) 2016, Přemysl Janouch <p.janouch@gmail.com>
-- Copyright (c) 2016, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -24,7 +23,7 @@ local cjson = require "cjson"
-- Setup configuration to load last.fm API credentials from
local user, api_key
degesch.setup_config {
xC.setup_config {
user = {
type = "string",
comment = "last.fm username",
@@ -118,24 +117,23 @@ end
local running
-- Initiate a connection to last.fm servers
async, await = xC.async, coroutine.yield
local make_request = function (buffer, action)
if not user or not api_key then
report_error (buffer, "configuration is incomplete")
return
end
if running then running.abort () end
running = degesch.connect ("ws.audioscrobbler.com", 80, {
on_success = function (c, host)
on_connected (buffer, c, host, action)
running = nil
end,
on_error = function (e)
if running then running:cancel () end
running = async.go (function ()
local c, host, e = await (async.dial ("ws.audioscrobbler.com", 80))
if e then
report_error (buffer, e)
running = nil
else
on_connected (buffer, c, host, action)
end
})
running = nil
end)
end
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -161,7 +159,7 @@ local send_song = function (buffer)
end
-- Hook input to simulate new commands
degesch.hook_input (function (hook, buffer, input)
xC.hook_input (function (hook, buffer, input)
if input == "/np" then
make_request (buffer, function (np)
now_playing = np

View File

@@ -1,11 +1,10 @@
--
-- ping-timeout.lua: ping timeout readability enhancement plugin
--
-- Copyright (c) 2015 - 2016, Přemysl Janouch <p.janouch@gmail.com>
-- Copyright (c) 2015 - 2016, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -16,8 +15,8 @@
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
degesch.hook_irc (function (hook, server, line)
local msg = degesch.parse (line)
xC.hook_irc (function (hook, server, line)
local msg = xC.parse (line)
local start, timeout = line:match ("^(.* :Ping timeout:) (%d+) seconds$")
if msg.command ~= "QUIT" or not start then
return line

68
plugins/xC/prime.lua Normal file
View File

@@ -0,0 +1,68 @@
--
-- prime.lua: highlight prime numbers in messages
--
-- Copyright (c) 2020, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
local smallest, highlight = 0, "\x1f"
xC.setup_config {
smallest = {
type = "integer",
default = "0",
comment = "smallest number to scan for primality",
on_change = function (v) smallest = math.max (v, 2) end
},
highlight = {
type = "string",
default = "\"\\x1f\"",
comment = "the attribute to use for highlights",
on_change = function (v) highlight = v end
},
}
-- The prime test is actually very fast, so there is no DoS concern
local do_intercolour = function (text)
return tostring (text:gsub ("%f[%w_]%d+", function (n)
if tonumber (n) < smallest then return nil end
for i = 2, n ^ (1 / 2) do if (n % i) == 0 then return nil end end
return highlight .. n .. highlight
end))
end
local do_interlink = function (text)
local rebuilt, last = {""}, 1
for start in text:gmatch ('()\x03') do
table.insert (rebuilt, do_intercolour (text:sub (last, start - 1)))
local sub = text:sub (start + 1)
last = start + (sub:match ('^%d%d?,%d%d?()') or sub:match ('^%d?%d?()'))
table.insert (rebuilt, text:sub (start, last - 1))
end
return table.concat (rebuilt) .. do_intercolour (text:sub (last))
end
local do_message = function (text)
local rebuilt, last = {""}, 1
for run, link, endpos in text:gmatch ('(.-)(%f[%g]https?://%g+)()') do
last = endpos
table.insert (rebuilt, do_interlink (run) .. link)
end
return table.concat (rebuilt) .. do_interlink (text:sub (last))
end
-- XXX: sadly it won't typically highlight primes in our own messages,
-- unless IRCv3 echo-message is on
xC.hook_irc (function (hook, server, line)
local start, message = line:match ("^(.- PRIVMSG .- :)(.*)$")
return message and start .. do_message (message) or line
end)

147
plugins/xC/slack.lua Normal file
View File

@@ -0,0 +1,147 @@
--
-- slack.lua: try to fix up UX when using the Slack IRC gateway
--
-- Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
local servers = {}
local read_servers = function (v)
servers = {}
for name in v:lower ():gmatch "[^,]+" do
servers[name] = true
end
end
-- This is a reverse list of Slack's automatic emoji, noseless forms
local unemojify, emoji, emoji_default = false, {}, {
heart = "<3",
broken_heart = "</3",
sunglasses = "8)",
anguished = "D:",
cry = ":'(",
monkey_face = ":o)",
kiss = ":*",
smiley = "=)",
smile = ":D",
wink = ";)",
laughing = ":>",
neutral_face = ":|",
open_mouth = ":o",
angry = ">:(",
slightly_smiling_face = ":)",
disappointed = ":(",
confused = ":/",
stuck_out_tongue = ":p",
stuck_out_tongue_winking_eye = ";p",
}
local load_emoji = function (extra)
emoji = {}
for k, v in pairs (emoji_default) do emoji[k] = v end
for k, v in extra:gmatch "([^,]+) ([^,]+)" do emoji[k] = v end
end
xC.setup_config {
servers = {
type = "string_array",
default = "\"\"",
comment = "list of server names that are Slack IRC gateways",
on_change = read_servers
},
unemojify = {
type = "boolean",
default = "true",
comment = "convert emoji to normal ASCII emoticons",
on_change = function (v) unemojify = v end
},
extra_emoji = {
type = "string_array",
default = "\"grinning :)),joy :'),innocent o:),persevere >_<\"",
comment = "overrides or extra emoji for unemojify",
on_change = function (v) load_emoji (v) end
}
}
-- We can handle external messages about what we've supposedly sent just fine,
-- so let's get rid of that "[username] some message sent from the web UI" crap
xC.hook_irc (function (hook, server, line)
local msg, us = xC.parse (line), server.user
if not servers[server.name] or msg.command ~= "PRIVMSG" or not us
or msg.params[1]:lower () ~= us.nickname:lower () then return line end
-- Taking a shortcut to avoid lengthy message reassembly
local quoted_nick = us.nickname:gsub ("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
local text = line:match ("^.- PRIVMSG .- :%[" .. quoted_nick .. "%] (.*)$")
if not text then return line end
return ":" .. us.nickname .. "!" .. server.irc_user_host .. " PRIVMSG "
.. msg.prefix:match "^[^!@]*" .. " :" .. text
end)
-- Unfuck emoji and :nick!nick@irc.tinyspeck.com MODE #channel +v nick : active
xC.hook_irc (function (hook, server, line)
if not servers[server.name] then return line end
if unemojify then
local start, text = line:match ("^(.- PRIVMSG .- :)(.*)$")
if start then return start .. text:gsub (":([a-z_]+):", function (name)
if emoji[name] then return emoji[name] end
return ":" .. name .. ":"
end) end
end
return line:gsub ("^(:%S+ MODE .+) : .*", "%1")
end)
-- The gateway simply ignores the NAMES command altogether
xC.hook_input (function (hook, buffer, input)
if not buffer.channel or not servers[buffer.server.name]
or not input:match "^/names%s*$" then return input end
local users = buffer.channel.users
table.sort (users, function (a, b)
if a.prefixes > b.prefixes then return true end
if a.prefixes < b.prefixes then return false end
return a.user.nickname < b.user.nickname
end)
local names = "Users on " .. buffer.channel.name .. ":"
for i, chan_user in ipairs (users) do
names = names .. " " .. chan_user.prefixes .. chan_user.user.nickname
end
buffer:log (names)
end)
xC.hook_completion (function (hook, data, word)
local chan = xC.current_buffer.channel
local server = xC.current_buffer.server
if not chan or not servers[server.name] then return end
-- In /commands there is typically no desire at all to add the at sign
if data.location == 1 and data.words[1]:match "^/" then return end
-- Handle both when the at sign is already there and when it is not
local needle = word:gsub ("^@", ""):lower ()
local t = {}
local try = function (name)
if data.location == 0 then name = name .. ":" end
if name:sub (1, #needle):lower () == needle then
table.insert (t, "@" .. name)
end
end
for _, chan_user in ipairs (chan.users) do
try (chan_user.user.nickname)
end
for _, special in ipairs { "channel", "here" } do
try (special)
end
return t
end)

View File

@@ -0,0 +1,27 @@
--
-- thin-cursor.lua: set a thin cursor
--
-- Copyright (c) 2016, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-- SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-- OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--
-- If tmux doesn't work, add the following to its configuration:
-- set -as terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q'
-- Change the "2" as per http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
local out = io.output ()
out:write ("\x1b[6 q"):flush ()
-- By registering a global variable, we get notified about plugin unload
x = setmetatable ({}, { __gc = function ()
out:write ("\x1b[2 q"):flush ()
end })

View File

@@ -1,11 +1,10 @@
--
-- utm-filter.lua: filter out Google Analytics bullshit from URLs
--
-- Copyright (c) 2015, Přemysl Janouch <p.janouch@gmail.com>
-- Copyright (c) 2015, Přemysl Eric Janouch <p@janouch.name>
--
-- Permission to use, copy, modify, and/or distribute this software for any
-- purpose with or without fee is hereby granted, provided that the above
-- copyright notice and this permission notice appear in all copies.
-- purpose with or without fee is hereby granted.
--
-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -53,11 +52,11 @@ local do_text = function (text)
return text:gsub ('%f[%g]https?://%g+', do_single_url)
end
degesch.hook_irc (function (hook, server, line)
xC.hook_irc (function (hook, server, line)
local start, message = line:match ("^(.* :)(.*)$")
return message and start .. do_text (message) or line
end)
degesch.hook_input (function (hook, buffer, input)
xC.hook_input (function (hook, buffer, input)
return do_text (input)
end)

8
test
View File

@@ -1,14 +1,14 @@
#!/usr/bin/expect -f
# Very basic end-to-end testing for Travis CI
# Very basic end-to-end testing for CI
# Run the daemon to test against
system ./kike --write-default-cfg
spawn ./kike -d
system ./xD --write-default-cfg
spawn ./xD -d
# 10 seconds is a bit too much
set timeout 5
spawn ./degesch
spawn ./xC
# Fuck this Tcl shit, I want the exit code
expect_after {

26
test-nick-colors Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
# Check whether the terminal colours filtered by our algorithm are legible
export example=$(
tcc "-run -lm" - <<-END
#include <stddef.h>
#include <stdio.h>
#include <math.h>
#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
$(perl -0777 -ne 'print $& if /^.*?\nfilter_color(?s:.*?)^}$/m' \
"$(dirname "$0")"/xC.c)
void main () {
size_t len = 0;
int *table = filter_color_cube_for_acceptable_nick_colors (&len);
for (size_t i = 0; i < len; i++)
printf ("<@\\x1b[38;5;%dmIRCuser\\x1b[m> I'm typing!\n", table[i]);
}
END
)
# Both should give acceptable results,
# which results in a bad compromise that the main author himself needs
xterm -bg black -fg white -e 'echo $example; cat' &
xterm -bg white -fg black -e 'echo $example; cat' &

14
test-static Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
# We don't use printf's percent notation with our custom logging mechanism,
# so the compiler cannot check it for us like it usually does
perl -n0777 - "$(dirname "$0")"/xC.c <<-'END'
while (/\blog_[^ ]+\s*\([^"()]*"[^"]*%[^%][^"]*"/gm) {
my ($p, $m) = ($`, $&);
printf "$ARGV:%d: suspicious log format string: %s...\n",
(1 + $p =~ tr/\n//), ($m =~ s/\s+/ /rg);
$status = 1;
}
END {
exit $status;
}
END

104
xB.adoc Normal file
View File

@@ -0,0 +1,104 @@
xB(1)
=====
:doctype: manpage
:manmanual: uirc3 Manual
:mansource: uirc3 {release-version}
Name
----
xB - modular IRC bot
Synopsis
--------
*xB* [_OPTION_]...
Description
-----------
*xB* is a modular IRC bot with a programming language-agnostic plugin
architecture based on co-processes.
Options
-------
*-d*, *--debug*::
Print more information to help debug various issues.
*-h*, *--help*::
Display a help message and exit.
*-V*, *--version*::
Output version information and exit.
*--write-default-cfg*[**=**__PATH__]::
Write a configuration file with defaults, show its path and exit.
+
The file will be appropriately commented.
Commands
--------
The bot accepts the following commands when they either appear quoted by the
*prefix* string on a channel or unquoted as a private message sent directly
to the bot, on the condition that the sending user matches the *admin*
regular expression or that it is left unset:
*quote* [_message_]::
Forwards the message to the IRC server as-is.
*quit* [_reason_]::
Quits the IRC server, with an optional reason string.
*status*::
Sends back a report about its state and all loaded plugins.
*load* _plugin_[, _plugin_]...::
Tries to load the given plugins.
*unload* _plugin_[, _plugin_]...::
Tries to unload the given plugins.
*reload* _plugin_[, _plugin_]...::
The same as *unload* immediately followed by *load*.
Plugins
-------
Plugins communicate with the bot over their standard input and output streams
using the IRC protocol. (Caveat: the standard C library doesn't automatically
flush FILE streams for pipes on newlines.) A special *XB* command is introduced
for RPC, with the following subcommands:
*XB get_config* _key_::
Request the value of the given configuration option. If no such option
exists, the value will be empty. The response will be delivered in
the following format:
+
```
XB :value
```
+
This is particularly useful for retrieving the *prefix* string.
*XB print* _message_::
Make the bot print the _message_ on its standard output.
*XB register*::
Once a plugin issues this command, it will start receiving all of the bot's
incoming IRC traffic, which includes data from the initialization period.
All other commands will be forwarded directly to the IRC server.
Files
-----
*xB* follows the XDG Base Directory Specification.
_~/.config/xB/xB.conf_::
The bot's configuration file. Use the *--write-default-cfg* option
to create a new one for editing.
_~/.local/share/xB/_::
The initial working directory for plugins, in which they may create private
databases or other files as needed.
_~/.local/share/xB/plugins/_::
_/usr/local/share/xB/plugins/_::
_/usr/share/xB/plugins/_::
Plugins are searched for in these directories, in order, unless
the *plugin_dir* configuration option overrides this.
Reporting bugs
--------------
Use https://git.janouch.name/p/uirc3 to report bugs, request features,
or submit pull requests.

View File

@@ -1,11 +1,10 @@
/*
* zyklonb.c: the experimental IRC bot
* xB.c: a modular IRC bot
*
* Copyright (c) 2014 - 2016, Přemysl Janouch <p.janouch@gmail.com>
* Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
@@ -18,8 +17,7 @@
*/
#include "config.h"
#define PROGRAM_NAME "ZyklonB"
#define PLUGIN_DIR ZYKLONB_PLUGIN_DIR
#define PROGRAM_NAME "xB"
#include "common.c"
@@ -27,9 +25,9 @@
static struct simple_config_item g_config_table[] =
{
{ "nickname", "ZyklonB", "IRC nickname" },
{ "nickname", "xB", "IRC nickname" },
{ "username", "bot", "IRC user name" },
{ "realname", "ZyklonB IRC bot", "IRC real name/e-mail" },
{ "realname", "xB IRC bot", "IRC real name/e-mail" },
{ "irc_host", NULL, "Address of the IRC server" },
{ "irc_port", "6667", "Port of the IRC server" },
@@ -50,7 +48,7 @@ static struct simple_config_item g_config_table[] =
{ "prefix", ":", "The prefix for bot commands" },
{ "admin", NULL, "Host mask for administrators" },
{ "plugins", NULL, "The plugins to load on startup" },
{ "plugin_dir", PLUGIN_DIR, "Where to search for plugins" },
{ "plugin_dir", NULL, "Plugin search path override" },
{ "recover", "on", "Whether to re-launch on crash" },
{ NULL, NULL, NULL }
@@ -83,22 +81,22 @@ struct plugin
struct str write_buffer; ///< Output yet to be sent out
};
static void
plugin_init (struct plugin *self)
static struct plugin *
plugin_new (void)
{
memset (self, 0, sizeof *self);
struct plugin *self = xcalloc (1, sizeof *self);
self->pid = -1;
str_init (&self->queued_output);
self->queued_output = str_make ();
self->read_fd = -1;
str_init (&self->read_buffer);
self->read_buffer = str_make ();
self->write_fd = -1;
str_init (&self->write_buffer);
self->write_buffer = str_make ();
return self;
}
static void
plugin_free (struct plugin *self)
plugin_destroy (struct plugin *self)
{
soft_assert (self->pid == -1);
free (self->name);
@@ -113,6 +111,8 @@ plugin_free (struct plugin *self)
if (!self->initialized)
str_free (&self->queued_output);
free (self);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -152,34 +152,33 @@ static void on_irc_reconnect_timeout (void *user_data);
static void
bot_context_init (struct bot_context *self)
{
str_map_init (&self->config);
self->config.free = free;
self->config = str_map_make (free);
simple_config_load_defaults (&self->config, g_config_table);
self->admin_re = NULL;
self->irc_fd = -1;
str_init (&self->read_buffer);
self->read_buffer = str_make ();
self->irc_registered = false;
self->ssl = NULL;
self->ssl_ctx = NULL;
self->plugins = NULL;
str_map_init (&self->plugins_by_name);
self->plugins_by_name = str_map_make (NULL);
poller_init (&self->poller);
self->quitting = false;
self->polling = false;
poller_timer_init (&self->timeout_tmr, &self->poller);
self->timeout_tmr = poller_timer_make (&self->poller);
self->timeout_tmr.dispatcher = on_irc_timeout;
self->timeout_tmr.user_data = self;
poller_timer_init (&self->ping_tmr, &self->poller);
self->ping_tmr = poller_timer_make (&self->poller);
self->ping_tmr.dispatcher = on_irc_ping_timeout;
self->ping_tmr.user_data = self;
poller_timer_init (&self->reconnect_tmr, &self->poller);
self->reconnect_tmr = poller_timer_make (&self->poller);
self->reconnect_tmr.dispatcher = on_irc_reconnect_timeout;
self->reconnect_tmr.user_data = self;
}
@@ -193,18 +192,13 @@ bot_context_free (struct bot_context *self)
str_free (&self->read_buffer);
// TODO: terminate the plugins properly before this is called
struct plugin *link, *tmp;
for (link = self->plugins; link; link = tmp)
{
tmp = link->next;
plugin_free (link);
free (link);
}
LIST_FOR_EACH (struct plugin, link, self->plugins)
plugin_destroy (link);
if (self->irc_fd != -1)
{
xclose (self->irc_fd);
poller_fd_reset (&self->irc_event);
xclose (self->irc_fd);
}
if (self->ssl)
SSL_free (self->ssl);
@@ -272,8 +266,7 @@ irc_send (struct bot_context *ctx, const char *format, ...)
return false;
va_start (ap, format);
struct str str;
str_init (&str);
struct str str = str_make ();
str_append_vprintf (&str, format, ap);
str_append (&str, "\r\n");
va_end (ap);
@@ -286,7 +279,7 @@ irc_send (struct bot_context *ctx, const char *format, ...)
if (SSL_write (ctx->ssl, str.str, str.len) != (int) str.len)
{
print_debug ("%s: %s: %s", __func__, "SSL_write",
ERR_error_string (ERR_get_error (), NULL));
xerr_describe_error ());
result = false;
}
}
@@ -310,7 +303,7 @@ irc_get_boolean_from_config
if (set_boolean_if_valid (value, str))
return true;
FAIL ("invalid configuration value for `%s'", name);
return error_set (e, "invalid configuration value for `%s'", name);
}
static bool
@@ -324,13 +317,15 @@ irc_initialize_ca_set (SSL_CTX *ssl_ctx, const char *file, const char *path,
if (SSL_CTX_load_verify_locations (ssl_ctx, file, path))
return true;
FAIL ("%s: %s", "failed to set locations for the CA certificate bundle",
ERR_reason_error_string (ERR_get_error ()));
return error_set (e, "%s: %s",
"failed to set locations for the CA certificate bundle",
xerr_describe_error ());
}
if (!SSL_CTX_set_default_verify_paths (ssl_ctx))
FAIL ("%s: %s", "couldn't load the default CA certificate bundle",
ERR_reason_error_string (ERR_get_error ()));
return error_set (e, "%s: %s",
"couldn't load the default CA certificate bundle",
xerr_describe_error ());
return true;
}
@@ -411,7 +406,7 @@ irc_initialize_tls (struct bot_context *ctx, struct error **e)
else if (!SSL_use_certificate_file (ctx->ssl, path, SSL_FILETYPE_PEM)
|| !SSL_use_PrivateKey_file (ctx->ssl, path, SSL_FILETYPE_PEM))
print_error ("%s: %s", "setting the TLS client certificate failed",
ERR_error_string (ERR_get_error (), NULL));
xerr_describe_error ());
free (path);
}
@@ -438,11 +433,9 @@ error_ssl_2:
SSL_CTX_free (ctx->ssl_ctx);
ctx->ssl_ctx = NULL;
error_ssl_1:
// XXX: these error strings are really nasty; also there could be
// multiple errors on the OpenSSL stack.
if (!error_info)
error_info = ERR_error_string (ERR_get_error (), NULL);
FAIL ("%s: %s", "could not initialize TLS", error_info);
error_info = xerr_describe_error ();
return error_set (e, "%s: %s", "could not initialize TLS", error_info);
}
static bool
@@ -455,7 +448,7 @@ irc_establish_connection (struct bot_context *ctx,
int err = getaddrinfo (host, port, &gai_hints, &gai_result);
if (err)
FAIL ("%s: %s: %s", "connection failed",
return error_set (e, "%s: %s: %s", "connection failed",
"getaddrinfo", gai_strerror (err));
int sockfd;
@@ -497,7 +490,7 @@ irc_establish_connection (struct bot_context *ctx,
freeaddrinfo (gai_result);
if (!gai_iter)
FAIL ("connection failed");
return error_set (e, "connection failed");
ctx->irc_fd = sockfd;
return true;
@@ -507,7 +500,7 @@ irc_establish_connection (struct bot_context *ctx,
static int g_signal_pipe[2]; ///< A pipe used to signal... signals
static struct str_vector
static struct strv
g_original_argv, ///< Original program arguments
g_recovery_env; ///< Environment for re-exec recovery
@@ -682,8 +675,8 @@ recovery_handler (int signum, siginfo_t *info, void *context)
static void
prepare_recovery_environment (void)
{
str_vector_init (&g_recovery_env);
str_vector_add_vector (&g_recovery_env, environ);
g_recovery_env = strv_make ();
strv_append_vector (&g_recovery_env, environ);
// Prepare a location within the environment where we will put the startup
// (or maybe rather restart) reason in case of an irrecoverable error.
@@ -700,7 +693,7 @@ prepare_recovery_environment (void)
else
{
g_startup_reason_location = g_recovery_env.vector + g_recovery_env.len;
str_vector_add (&g_recovery_env, "");
strv_append (&g_recovery_env, "");
}
}
@@ -742,7 +735,7 @@ setup_recovery_handler (struct bot_context *ctx, struct error **e)
// --- Plugins -----------------------------------------------------------------
/// The name of the special IRC command for interprocess communication
static const char *plugin_ipc_command = "ZYKLONB";
static const char *plugin_ipc_command = "XB";
static struct plugin *
plugin_find_by_pid (struct bot_context *ctx, pid_t pid)
@@ -962,7 +955,7 @@ on_plugin_readable (const struct pollfd *fd, struct plugin *plugin)
struct str *buf = &plugin->read_buffer;
while (true)
{
str_ensure_space (buf, 512 + 1);
str_reserve (buf, 512 + 1);
ssize_t n_read = read (fd->fd, buf->str + buf->len,
buf->alloc - buf->len - 1);
@@ -1021,16 +1014,41 @@ is_valid_plugin_name (const char *name)
return true;
}
static char *
plugin_resolve_relative_filename (const char *filename)
{
struct strv paths = strv_make ();
get_xdg_data_dirs (&paths);
char *result = resolve_relative_filename_generic
(&paths, PROGRAM_NAME "/plugins/", filename);
strv_free (&paths);
return result;
}
static struct plugin *
plugin_launch (struct bot_context *ctx, const char *name, struct error **e)
{
char *path = NULL;
const char *plugin_dir = str_map_find (&ctx->config, "plugin_dir");
if (!plugin_dir)
FAIL ("plugin directory not set");
if (plugin_dir)
{
// resolve_relative_filename_generic() won't accept relative paths,
// so just keep the old behaviour and expect the file to exist.
// We could use resolve_filename() on "plugin_dir" with paths=getcwd().
path = xstrdup_printf ("%s/%s", plugin_dir, name);
}
else if (!(path = plugin_resolve_relative_filename (name)))
{
error_set (e, "plugin not found");
goto fail_0;
}
int stdin_pipe[2];
if (pipe (stdin_pipe) == -1)
FAIL ("%s: %s", "pipe", strerror (errno));
{
error_set (e, "%s: %s", "pipe", strerror (errno));
goto fail_0;
}
int stdout_pipe[2];
if (pipe (stdout_pipe) == -1)
@@ -1039,8 +1057,7 @@ plugin_launch (struct bot_context *ctx, const char *name, struct error **e)
goto fail_1;
}
struct str work_dir;
str_init (&work_dir);
struct str work_dir = str_make ();
get_xdg_home_dir (&work_dir, "XDG_DATA_HOME", ".local/share");
str_append_printf (&work_dir, "/%s", PROGRAM_NAME);
@@ -1080,7 +1097,7 @@ plugin_launch (struct bot_context *ctx, const char *name, struct error **e)
// Restore some of the signal handling
signal (SIGPIPE, SIG_DFL);
char *argv[] = { xstrdup_printf ("%s/%s", plugin_dir, name), NULL };
char *argv[] = { path, NULL };
execve (argv[0], argv, environ);
// We will collect the failure later via SIGCHLD
@@ -1090,12 +1107,12 @@ plugin_launch (struct bot_context *ctx, const char *name, struct error **e)
}
str_free (&work_dir);
free (path);
xclose (stdin_pipe[0]);
xclose (stdout_pipe[1]);
struct plugin *plugin = xmalloc (sizeof *plugin);
plugin_init (plugin);
struct plugin *plugin = plugin_new ();
plugin->ctx = ctx;
plugin->pid = pid;
plugin->name = xstrdup (name);
@@ -1110,6 +1127,8 @@ fail_2:
fail_1:
xclose (stdin_pipe[0]);
xclose (stdin_pipe[1]);
fail_0:
free (path);
return NULL;
}
@@ -1117,9 +1136,9 @@ static bool
plugin_load (struct bot_context *ctx, const char *name, struct error **e)
{
if (!is_valid_plugin_name (name))
FAIL ("invalid plugin name");
return error_set (e, "invalid plugin name");
if (str_map_find (&ctx->plugins_by_name, name))
FAIL ("the plugin has already been loaded");
return error_set (e, "the plugin has already been loaded");
struct plugin *plugin;
if (!(plugin = plugin_launch (ctx, name, e)))
@@ -1128,11 +1147,11 @@ plugin_load (struct bot_context *ctx, const char *name, struct error **e)
set_blocking (plugin->read_fd, false);
set_blocking (plugin->write_fd, false);
poller_fd_init (&plugin->read_event, &ctx->poller, plugin->read_fd);
plugin->read_event = poller_fd_make (&ctx->poller, plugin->read_fd);
plugin->read_event.dispatcher = (poller_fd_fn) on_plugin_readable;
plugin->read_event.user_data = plugin;
poller_fd_init (&plugin->write_event, &ctx->poller, plugin->write_fd);
plugin->write_event = poller_fd_make (&ctx->poller, plugin->write_fd);
plugin->write_event.dispatcher = (poller_fd_fn) on_plugin_writable;
plugin->write_event.user_data = plugin;
@@ -1149,7 +1168,7 @@ plugin_unload (struct bot_context *ctx, const char *name, struct error **e)
struct plugin *plugin = str_map_find (&ctx->plugins_by_name, name);
if (!plugin)
FAIL ("no such plugin is loaded");
return error_set (e, "no such plugin is loaded");
plugin_zombify (plugin);
@@ -1165,10 +1184,8 @@ plugin_load_all_from_config (struct bot_context *ctx)
if (!plugin_list)
return;
struct str_vector plugins;
str_vector_init (&plugins);
cstr_split_ignore_empty (plugin_list, ',', &plugins);
struct strv plugins = strv_make ();
cstr_split (plugin_list, ",", true, &plugins);
for (size_t i = 0; i < plugins.len; i++)
{
char *name = cstr_strip_in_place (plugins.vector[i], " ");
@@ -1181,7 +1198,7 @@ plugin_load_all_from_config (struct bot_context *ctx)
}
}
str_vector_free (&plugins);
strv_free (&plugins);
}
// --- Main program ------------------------------------------------------------
@@ -1206,13 +1223,13 @@ parse_bot_command (const char *s, const char *command, const char **following)
}
static void
split_bot_command_argument_list (const char *arguments, struct str_vector *out)
split_bot_command_argument_list (const char *arguments, struct strv *out)
{
cstr_split_ignore_empty (arguments, ',', out);
cstr_split (arguments, ",", true, out);
for (size_t i = 0; i < out->len; )
{
if (!*cstr_strip_in_place (out->vector[i], " \t"))
str_vector_remove (out, i);
strv_remove (out, i);
else
i++;
}
@@ -1248,10 +1265,8 @@ respond_to_user (struct bot_context *ctx, const struct irc_message *msg,
strncpy (nick, msg->prefix, sizeof nick - 1);
nick[sizeof nick - 1] = '\0';
struct str text;
va_list ap;
str_init (&text);
struct str text = str_make ();
va_start (ap, format);
str_append_vprintf (&text, format, ap);
va_end (ap);
@@ -1312,9 +1327,7 @@ process_plugin_reload (struct bot_context *ctx,
static char *
make_status_report (struct bot_context *ctx)
{
struct str report;
str_init (&report);
struct str report = str_make ();
const char *reason = getenv (g_startup_reason_str);
if (!reason)
reason = "launched normally";
@@ -1359,8 +1372,7 @@ process_privmsg (struct bot_context *ctx, const struct irc_message *msg)
return;
const char *following;
struct str_vector list;
str_vector_init (&list);
struct strv list = strv_make ();
if (parse_bot_command (text, "quote", &following))
// This seems to replace tons of random stupid commands
@@ -1400,7 +1412,7 @@ process_privmsg (struct bot_context *ctx, const struct irc_message *msg)
process_plugin_unload (ctx, msg, list.vector[i]);
}
str_vector_free (&list);
strv_free (&list);
}
static void
@@ -1577,13 +1589,11 @@ on_irc_disconnected (struct bot_context *ctx)
ctx->ssl_ctx = NULL;
}
poller_fd_reset (&ctx->irc_event);
xclose (ctx->irc_fd);
ctx->irc_fd = -1;
ctx->irc_registered = false;
ctx->irc_event.closed = true;
poller_fd_reset (&ctx->irc_event);
// TODO: inform plugins about the disconnect event
// All of our timers have lost their meaning now
@@ -1638,7 +1648,7 @@ on_irc_readable (const struct pollfd *fd, struct bot_context *ctx)
bool disconnected = false;
while (true)
{
str_ensure_space (buf, 512);
str_reserve (buf, 512);
switch (fill_buffer (ctx, buf))
{
case IRC_READ_AGAIN:
@@ -1778,7 +1788,7 @@ irc_connect (struct bot_context *ctx, struct error **e)
// TODO: again, get rid of `struct error' in here. The question is: how
// do we tell our caller that he should not try to reconnect?
if (!irc_host)
FAIL ("no hostname specified in configuration");
return error_set (e, "no hostname specified in configuration");
bool use_tls;
if (!irc_get_boolean_from_config (ctx, "tls", &use_tls, e))
@@ -1799,7 +1809,7 @@ irc_connect (struct bot_context *ctx, struct error **e)
}
print_status ("connection established");
poller_fd_init (&ctx->irc_event, &ctx->poller, ctx->irc_fd);
ctx->irc_event = poller_fd_make (&ctx->poller, ctx->irc_fd);
ctx->irc_event.dispatcher = (poller_fd_fn) on_irc_readable;
ctx->irc_event.user_data = ctx;
@@ -1823,7 +1833,10 @@ parse_config (struct bot_context *ctx, struct error **e)
const char *delay_str = str_map_find (&ctx->config, "reconnect_delay");
hard_assert (delay_str != NULL); // We have a default value for this
if (!xstrtoul (&ctx->reconnect_delay, delay_str, 10))
FAIL ("invalid configuration value for `%s'", "reconnect_delay");
{
return error_set (e,
"invalid configuration value for `%s'", "reconnect_delay");
}
hard_assert (!ctx->admin_re);
const char *admin = str_map_find (&ctx->config, "admin");
@@ -1847,7 +1860,7 @@ on_plugin_death (struct plugin *plugin, int status)
struct bot_context *ctx = plugin->ctx;
// TODO: callbacks on children death, so that we may tell the user
// "plugin `name' died like a dirty jewish pig"; use `status'
// "plugin `name' died"; use `status'
if (!plugin->is_zombie && WIFSIGNALED (status))
{
const char *notes = "";
@@ -1877,8 +1890,7 @@ on_plugin_death (struct plugin *plugin, int status)
plugin->read_fd = -1;
LIST_UNLINK (ctx->plugins, plugin);
plugin_free (plugin);
free (plugin);
plugin_destroy (plugin);
// Living child processes block us from quitting
try_finish_quit (ctx);
@@ -1956,8 +1968,8 @@ on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)
int
main (int argc, char *argv[])
{
str_vector_init (&g_original_argv);
str_vector_add_vector (&g_original_argv, argv);
g_original_argv = strv_make ();
strv_append_vector (&g_original_argv, argv);
static const struct opt opts[] =
{
@@ -1970,8 +1982,8 @@ main (int argc, char *argv[])
{ 0, NULL, NULL, 0, NULL }
};
struct opt_handler oh;
opt_handler_init (&oh, argc, argv, opts, NULL, "Experimental IRC bot.");
struct opt_handler oh =
opt_handler_make (argc, argv, opts, NULL, "Modular IRC bot.");
int c;
while ((c = opt_handler_get (&oh)) != -1)
@@ -1999,12 +2011,7 @@ main (int argc, char *argv[])
print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");
setup_signal_handlers ();
SSL_library_init ();
atexit (EVP_cleanup);
SSL_load_error_strings ();
// XXX: ERR_load_BIO_strings()? Anything else?
atexit (ERR_free_strings);
init_openssl ();
struct bot_context ctx;
bot_context_init (&ctx);
@@ -2018,7 +2025,7 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
poller_fd_init (&ctx.signal_event, &ctx.poller, g_signal_pipe[0]);
ctx.signal_event = poller_fd_make (&ctx.poller, g_signal_pipe[0]);
ctx.signal_event.dispatcher = (poller_fd_fn) on_signal_pipe_readable;
ctx.signal_event.user_data = &ctx;
poller_fd_set (&ctx.signal_event, POLLIN);
@@ -2050,7 +2057,7 @@ main (int argc, char *argv[])
poller_run (&ctx.poller);
bot_context_free (&ctx);
str_vector_free (&g_original_argv);
strv_free (&g_original_argv);
return EXIT_SUCCESS;
}

127
xC.adoc Normal file
View File

@@ -0,0 +1,127 @@
xC(1)
=====
:doctype: manpage
:manmanual: uirc3 Manual
:mansource: uirc3 {release-version}
Name
----
xC - terminal-based IRC client
Synopsis
--------
*xC* [_OPTION_]...
Description
-----------
*xC* is a scriptable IRC client for the command line. On the first run it will
welcome you with an introductory message. Should you ever get lost, use the
*/help* command to obtain more information on commands or options.
Options
-------
*-f*, *--format*::
Format IRC text from the standard input, converting colour sequences and
other formatting marks to ANSI codes retrieved from the *terminfo*(5)
database:
+
```
printf '\x02bold\x02\n' | xC -f
```
+
This feature may be used to preview server MOTD files.
*-h*, *--help*::
Display a help message and exit.
*-V*, *--version*::
Output version information and exit.
Key bindings
------------
Most key bindings are inherited from the frontend in use, which is either GNU
Readline or BSD editline. A few of them, however, are special to the IRC client
or assume a different function. This is a list of all local overrides and
their respective function names:
*M-p*::
Go up in history for this buffer (normally mapped to *C-p*).
*M-n*::
Go down in history for this buffer (normally mapped to *C-n*).
*C-p*, *F5*: *previous-buffer*::
Switch to the previous buffer in order.
*C-n*, *F6*: *next-buffer*::
Switch to the next buffer in order.
*M-TAB*: *switch-buffer*::
Switch to the last buffer, i.e., the one you were in before.
*M-0*, *M-1*, ..., *M-9*: *goto-buffer*::
Go to the N-th buffer (normally sets a repeat counter).
Since there is no buffer number zero, *M-0* goes to the tenth one.
*M-!*: *goto-highlight*::
Go to the first following buffer with an unseen highlight.
*M-a*: *goto-activity*::
Go to the first following buffer with unseen activity.
*PageUp*: *display-backlog*::
Show the in-memory backlog for this buffer in the backlog helper,
which is almost certainly the *less*(1) program.
*M-h*: *display-full-log*::
Show the log file for this buffer in the backlog helper.
*M-H*: *toggle-unimportant*::
Hide all join, part and quit messages, as well as all channel mode changes
that only relate to user channel modes. Intended to reduce noise in
channels with lots of people.
*M-e*: *edit-input*::
Run an editor on the command line, making it easy to edit multiline
messages. Remember to save the file before exit.
*M-m*: *insert-attribute*::
The next key will be interpreted as a formatting mark to insert:
*c* for colours (optionally followed by numbers for the foreground
and background), *i* for italics, *b* for bold text, *u* for underlined,
*x* for struck-through, *v* for inverse text and *o* resets all formatting.
*C-l*: *redraw-screen*::
Should there be any issues with the display, this will clear the terminal
screen and redraw all information.
Additionally, *C-w* and *C-u* in editline behave the same as they would in
Readline or the "vi" command mode, even though the "emacs" mode is enabled
by default.
Bindings can be customized in your _.inputrc_ or _.editrc_ file. Both libraries
support conditional execution based on the program name. Beware that it is easy
to make breaking changes.
Environment
-----------
*VISUAL*, *EDITOR*::
The editor program to be launched by the *edit-input* function.
If neither variable is set, it defaults to *vi*(1).
Files
-----
*xC* follows the XDG Base Directory Specification.
_~/.config/xC/xC.conf_::
The program's configuration file. Preferrably use internal facilities, such
as the */set* command, to make changes in it.
_~/.local/share/xC/logs/_::
When enabled by *behaviour.logging*, log files are stored here.
_~/.local/share/xC/plugins/_::
_/usr/local/share/xC/plugins/_::
_/usr/share/xC/plugins/_::
Plugins are searched for in these directories, in order.
Bugs
----
The editline (libedit) frontend is more of a proof of concept that mostly seems
to work but exhibits bugs that are not our fault.
Reporting bugs
--------------
Use https://git.janouch.name/p/uirc3 to report bugs, request features,
or submit pull requests.
See also
--------
*less*(1), *readline*(3) or *editline*(7)

File diff suppressed because it is too large Load Diff

BIN
xC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@@ -2,7 +2,7 @@
LC_ALL=C exec awk '
BEGIN {
# The message catalog is a by-product
msg = "kike.msg"
msg = "xD.msg"
print "$quote \"" > msg;
print "$set 1" > msg;
}

View File

@@ -85,3 +85,9 @@
482 IRC_ERR_CHANOPRIVSNEEDED "%s :You're not channel operator"
501 IRC_ERR_UMODEUNKNOWNFLAG ":Unknown MODE flag"
502 IRC_ERR_USERSDONTMATCH ":Cannot change mode for other users"
902 IRC_ERR_NICKLOCKED ":You must use a nick assigned to you"
903 IRC_RPL_SASLSUCCESS ":SASL authentication successful"
904 IRC_ERR_SASLFAIL ":SASL authentication failed"
905 IRC_ERR_SASLTOOLONG ":SASL message too long"
906 IRC_ERR_SASLABORTED ":SASL authentication aborted"
907 IRC_ERR_SASLALREADY ":You have already authenticated using SASL"

53
xD.adoc Normal file
View File

@@ -0,0 +1,53 @@
xD(1)
=====
:doctype: manpage
:manmanual: uirc3 Manual
:mansource: uirc3 {release-version}
Name
----
xD - IRC daemon
Synopsis
--------
*xD* [_OPTION_]...
Description
-----------
*xD* is a basic IRC daemon for single-server networks, suitable for testing
and private use. When run without a configuration file, it will start listening
on the standard port 6667 and the "any" address.
Options
-------
*-d*, *--debug*::
Do not daemonize, print more information on the standard error stream
to help debug various issues.
*-h*, *--help*::
Display a help message and exit.
*-V*, *--version*::
Output version information and exit.
*--write-default-cfg*[**=**__PATH__]::
Write a configuration file with defaults, show its path and exit.
+
The file will be appropriately commented.
+
When no _PATH_ is specified, it will be created in the user's home directory,
contrary to what you might expect from a server.
Files
-----
*xD* follows the XDG Base Directory Specification.
_~/.config/xD/xD.conf_::
_/etc/xdg/xD/xD.conf_::
The daemon's configuration file. Use the *--write-default-cfg* option
to create a new one for editing.
Reporting bugs
--------------
Use https://git.janouch.name/p/uirc3 to report bugs, request features,
or submit pull requests.

File diff suppressed because it is too large Load Diff