| Commit message (Collapse) | Author | Age | Files | Lines |
... | |
|
|
|
|
|
|
|
|
|
| |
Currently it's not possible to define combinations of marked and
searched. Since searched messages are just a convenience, while marked
message can be operated upon, make sure that when a message is both
marked and a search result the marked styleset is applied.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
|
|
|
| |
The introduction of the iterator means the "next" non-deleted message is
never nil, it will always be equal to the previous message (meaning
there is only one message left and it is the one we are deleting). In
this case, deliberately set next to nil so that the remove tab on delete
logic works properly.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Refactor split update logic to more simply update the split. Through the
evolution of the split logic, additional variables were stored within
the account which allows for cleaner updating of the split.
Compare selected UID instead of pointer to message when deciding not to
update split.
Allow splits to be created and closed when no message is selected. The
split will be filled with a ui.Fill (blank). The user will only see a
border at the split location when no message is selected.
Rename clearSplit to closeSplit, as it is only used in the case when the
user doesn't want a split anymore.
Ensure that the selected UID is reset to the magic UID when there are no
messages left in the message store.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The maildir worker sends a MessagesMoved message to the UI when messages
are moved, enabling the destination directory to update it's counts. The
filesystem watcher sees the move and updates the directory currently
selected. However, an update of the UIDs in the msgstore is not
completed as it is in other workers. All other works, via some mechanism
(direct or EXPUNGE update) issue a MessagesDeleted message after a move.
Send this message to the UI for the maildir worker to have it work as
all other workers do.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
| |
Add indicator of an attachment to the flags and make the character used
to be configurable.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Tested-by: Jens Grassel <jens@wegtam.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
There is only one instance of AercConfig which is associated to the Aerc
widget. Everywhere we need to access configuration options, we need
somehow to get a reference either to the Aerc widget or to a pointer to
the AercConfig instance. This makes the code cluttered.
Remove the AercConfig structure and every place where it is referenced.
Instead, declare global variables for every configuration section and
access them directly from the `config` module.
Since bindings and ui sections can be "contextual" (i.e. per account,
per folder or per subject), leave most local references intact.
Replacing them with config.{Ui,Binds}.For{Account,Folder,Subject} would
make this patch even more unreadable. This is something that may be
addressed in the future.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The current contextual binds and ui config API is awkward and cumbersome
to use. Rework it to make it more elegant.
Store the contextual sections as private fields of the UIConfig and
KeyBindings structures. Add cache to avoid recomputation of the composed
UIConfig and KeyBindings objects every time a contextual item is
requested. Replace the cache from DirectoryList with that.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
| |
Process shortlog stats for Acked-by, Reviewed-by and Tested-by trailers.
Signed-off-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add a new :multipart command that can be executed on the composer review
screen. This command takes a MIME type as argument which needs to match
a setting in the new [multipart-converters] section of aerc.conf. A part
can be removed by using the -d flag.
The [multipart-converters] section has MIME types associated with
commands. These commands are executed with sh -c every time the main
email body is updated to generate each part content. The commands are
expected to output valid UTF-8 text.
If a command fails, an explicit error will be printed next to the part
MIME type to allow users to debug their issue but the email may still be
sent anyway with an empty alternative part.
This is mostly intended for people who *really* need to send html
messages for their boss or for corporate reasons. For now, it is
a manual and explicit action to convert a message in such a way.
Here is an example configuration:
[multipart-converters]
text/html = pandoc -f markdown -t html
And the associated binding to append an HTML alternative to a message:
[compose::review]
H = :multipart text/html<enter>
hh = :multipart -d text/html<enter>
Link: https://lists.sr.ht/~rjarry/aerc-discuss/%3CCO5KH4W57XNB.2PZLR1CNFK22H%40mashenka%3E
Co-authored-by: Eric McConville <emcconville@emcconville.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Commit b46e57324394 ("store: fix server-side threads toggling")
introduced a regression where the uids of the store were not set after
rebuilding the threads. This would result in a flash of pending messages
as they were repopulated. This also would not properly remove messages
which were moved out of the store (deleted, moved, archived) with server
side threading.
Re-add the line that sets the store.uids.
Fixes: b46e57324394 ("store: fix server-side threads toggling")
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
|
| |
When using aerc > log, print all messages as it was before the logging
system cleanup.
Requested-by: Tim Culverhouse <tim@timculverhouse.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
|
|
|
| |
Change the default provider to gpg unless the internal keyring is
initialized and contains one key.
This should be more user friendly.
Link: https://lists.sr.ht/~rjarry/aerc-discuss/%3CCO783CI3IU9F.184DBQTPMIPBS%40paul%3E
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
| |
Instead of obscure descriptions for the settings format, add a synopsis
for all settings in the man pages.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I had started writing this as an awk script but quickly got stuck with
obscure code which did not even work properly. I jumped the gun and re
did it in go. Bonus, we will not have MacOS's 1987 BSD awk issues. On
the other hand, instead of a 20.0K awk script, we now have a 2.2M static
go binary. If this makes people scream, I challenge them to do that with
BSD awk :)
Basically, this takes text from stdin or from a file and wraps long
lines on word boundaries. It takes care of not breaking up email quotes
nor list items (numbered as well). Also, it is conservative by default
and only wraps long lines and lines that end with a space (indicating
a format=flowed message).
If the AERC_SUBJECT environment variable is defined and contains the
word PATCH, the text is not modified at all (i.e. wrap behaves as
cat(1)).
There are a few command line options to control behavior:
Usage of ./wrap:
-f string
read from file instead of stdin
-l int
minimum percentage of letters in a line to be considered
a paragaph (default 50)
-r reflow all paragraphs even if no trailing space
-w int
preferred wrap margin (default 80)
Update docs, makefile and default config file with examples.
Add a torture test to ensure it works as expected.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
| |
It may help understanding what is currently going on with go 1.19.
Signed-off-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
| |
Currently compose will always show only some hardcoded default commands.
If hardcoded command is not bound to any key remove it from the list. If
user adds new bindings to compose::review add them - without help text
- at the bottom.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
| |
Opening an email to view and then to reply will have two tabs open,
and after a reply the view tab needs to be closed manually. Allow the
user to set a close-on-reply option that will close the viewer tab when
replying and reopen the viewer tab in case the reply is not sent.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Tested-by: Inwit <inwit@sindominio.net>
Signed-off-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
| |
Use list-status to perform check-mail commands, if it is available. This
provides a significant performance benefit by only requiring one IMAP
command vs one command for each mailbox.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
| |
Use the LIST-STATUS extension when listing directories. This enables the
UI to show message counts for every mailbox, in a similar behavior to
the maildir and notmuch backends.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Check for LIST-STATUS capability on IMAP servers. This will be used in
subsequent commits for improved check-mail performance.
The LIST-STATUS command allows the LIST command to also return STATUS
responses, which include message counts.
Upgrade go-imap to latest release. Add go-imap-liststatus extension.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Add IMAP extension LIST-STATUS handling to IMAP worker.
Reference: https://www.rfc-editor.org/rfc/rfc5819.html
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Simplify the error handling in ListDirectories so the Done message is
not within an else statement
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
| |
JWZ is used for client side threading, as well as maildir backend
threading. A bug was fixed in the 'develop' branch to address a panic
caused by some emails. Upgrade to the 'develop' branch which includes
this bugfix.
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
|
|
|
| |
Fix server-side threads toggling that can sometimes cause the uids to be
out-of-sync with the threads. This patch ensures a consistent way of
handling the uids and threads in the store.
Fixes: https://todo.sr.ht/~rjarry/aerc/102
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Keep the original tags when a message is moved from one maildir folder to
another.
Signed-off-by: inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
| |
In notmuch, a message can be represented by several identical files across the
maildir structure, which makes the operation of moving a message from a virtual
folder (showing a notmuch query) to a maildir folder problematic if the number
of files for that message is greater than 1. Since the move operation is
unambiguous when the message is represented by a single file, allow moving
messages in such cases.
Signed-off-by: inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Support relative terms when writing date ranges in the search and filter
commands with the -d flag. Syntax is inspired by the notmuch search
terms.
Terms can be written with spaces or underscores for a better
readability, so both "this_week" and "this week" are allowed. Terms are
not case-sensitive.
Some terms can be prefixed with either "this" or "last" where applicable
("this" is assumed by default if omitted):
- "today", "yesterday"
- ("this"|"last") "year", "month", "week"
- all weekdays (e.g. "Tuesday", "last_wed")
- all months (e.g. "January", "last_feb")
Note that "month" should always be spelled out to prevent a possible
ambiguity with "Monday".
Weekdays and months do not need to be written out completely, i.e.
"February..March" and "Feb..Mar" are both understood.
Relative date terms can be used with the <N (year|month|week|day)>
syntax where N is a positive integer indicating the number of time units
in the past from today. The units can be abbreviated with a single
letter, e.g. "1w 1d.." is the same as "1 week 1 day..".
More examples:
:filter -d yesterday
:filter -d last_monday..
:filter -d mon..sat
:filter -d 1y1m1w1d..
:search -d this_week "PATCH aerc"
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
| |
Filter and search with a date range in the imap backend.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
| |
Filter and search with a date range in the maildir backend.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Handle date ranges in the filter and search commands for searching and
filtering based on the Date: header. Implement a flag (-d) that accepts
a date range <start[..end]> where the start date is included in the
range but the end date is not, i.e. [start,end).
The start or end date can be omitted: "start", "start..", "..end", or
"start..end" are all valid inputs.
An example filter query would look like this: :filter -d 2022-11-09..
The dates should be in the YYYY-MM-DD format.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
| |
Use the same name than the builtin "log" package. That way, we do not
risk logging in the wrong place.
Suggested-by: Tim Culverhouse <tim@timculverhouse.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The main goal is to ensure that by default, the log file (if configured)
does not grow out of proportions. Most of the logging messages in aerc
are actually for debugging and/or trace purposes.
Define clear rules for logging levels. Enforce these rules everywhere.
After this patch, here is what the log file looks like after starting up
with a single account:
INFO 2022/11/24 20:26:16.147164 aerc.go:176: Starting up version 0.13.0-100-g683981479c60 (go1.18.7 amd64 linux)
INFO 2022/11/24 20:26:17.546448 account.go:254: [work] connected.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
|
| |
I don't see a point in logging with UTC time. Also, it can be confusing
since this is not explicit in the date format.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
| |
That may be useful to debug some filter matchers.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
| |
We need more logging granularity.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This modifies the colorize script to accept a command line parameter to
change the colour theme. Currently only a solarized version is added.
Due to the nature of awk the theme has to be defined via the `-v` flag.
Due to the `switch` statement only being available in GNU awk we use a
`if else` statement to ensure that the default colours are used if
either the `THEME` variable is not set at all or set to `default`.
Solarized colour scheme: https://ethanschoonover.com/solarized/
Signed-off-by: Jens Grassel <jens@wegtam.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Add more detailed explanation of how filters work and substantially
increase number and detail of examples.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Using KeysHash() does not guarantee any stable ordering of elements.
This causes random weirdness when some filters are using wildcards:
[filters]
text/plain=colorize
text/html=html
text/*=bat -fP --file-name="$AERC_FILENAME" --style=plain
When the source order is not preserved, the text/* filter may be matched
first against text/plain or text/html parts.
Use Keys() which does not create a map and preserve original ordering.
Fixes: 17bb9387c4a3 ("config: move [filters] parsing to separate file")
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
aerc-config(5) is getting too big and cluttered. Only keep aerc.conf
settings in it. Move binds.conf settings in aerc-binds(5) and
accounts.conf settings in aerc-accounts(5).
Adjust all references accordingly. Update the README to reference the
two new man pages. Update the Makefile to install them in proper
locations.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
|
|
|
|
|
|
|
|
| |
Mention that multiple marked messages are reordered if they look like
a patch series.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
For consistent rendering, it is best if every man page uses the same
conventions. These are completely arbitrary and I only did some trial
& error until I found something that looked visually OK.
Update CONTRIBUTING.md with guidelines for scdoc markup conventions.
Update all man pages according to these guidelines.
Suggested-by: Inwit <inwit@sindominio.net>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Inwit <inwit@sindominio.net>
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Remove `Default: none` or `Default: ""` statements which may be
confusing. If there is no default value, simply do not mention a default
value.
Comment all settings in aerc.conf so that real default values are used.
Adjust man pages.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
|
|
|
|
|
|
|
|
|
| |
Apparently the canon dictates that the program name be capitalized at
the top of each man page. The same doctrine seems to apply to section
headings as well.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
- Add missing periods at the end of sentences.
- Add missing setting names before examples.
- Remove double spaces after periods.
- Fix sendmail default path.
- Add missing [filters] examples in aerc-config(5).
- Add missing log-file config hint in aerc(1).
- Fix title and default filters in aerc-tutorial(7).
- Add missing empty lines after section headings.
- Fix various typos.
- Remove duplicate section in aerc-search(1).
Signed-off-by: Inwit <inwit@sindominio.net>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fix the following error occurring when fetching new headers after IDLE:
panic: runtime error: invalid memory address or nil pointer dereference
bufio.(*Reader).fill(0xc00029af00)
bufio/bufio.go:106 +0xd8
bufio.(*Reader).Peek(0xc0006e1f00, 0x1)
bufio/bufio.go:144 +0x5d
github.com/emersion/go-message/textproto.ReadHeader(0x0?)
github.com/emersion/go-message@v0.15.0/textproto/header.go:525 +0x5f
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessageHeaders.func1(0xc00026e980)
git.sr.ht/~rjarry/aerc/worker/imap/fetch.go:49 +0x1ab
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages.func1()
git.sr.ht/~rjarry/aerc/worker/imap/fetch.go:222 +0xa2
created by git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages
git.sr.ht/~rjarry/aerc/worker/imap/fetch.go:217 +0x10a
Some IMAP messages only contain flag updates, ignore them.
Reuse the same logic than handleFetchMessageBodyPart() and
handleFechFullMessages().
Fixes: https://todo.sr.ht/~rjarry/aerc/111
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: normen <normenweb@mac.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Fix popover menu regression by triggering completions after the
simulated key strokes have been processed.
Commit 055c6dc6604f7f ("exline: don't draw completions for keybinds")
removed a double-draw event and reinstated the tab completions correctly
but it did not trigger the completion process again which was the
expected behavior.
Fixes: https://todo.sr.ht/~rjarry/aerc/104
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Mention the header Labels (for showing notmuch tags in the viewer) in the
docs.
Signed-off-by: inwit <inwit@sindominio.net>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
|
|
|
|
|
|
|
|
| |
Fix parts index by making an explicit copy of the index slice.
Fixes: https://todo.sr.ht/~rjarry/aerc/103
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Currently we have tab to go forward among the tabs, but not backwards.
Add backtab bound to prev-field to make the defaults symmetrical.
Signed-off-by: Bence Ferdinandy <bence@ferdinandy.com>
Acked-by: Robin Jarry <robin@jarry.cc>
|
|
|
|
|
|
|
|
| |
Draw a framed box with a title containing an interactive-drawable
widget.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Tim Culverhouse <tim@timculverhouse.com>
|