aboutsummaryrefslogtreecommitdiff
path: root/tw/services/files/aerc
diff options
context:
space:
mode:
authorTimo Wilken2024-03-09 14:52:56 +0100
committerTimo Wilken2024-03-10 16:19:00 +0100
commitde20fc8d904643ffe6957febfc6a24e57c12b512 (patch)
tree8177459e40786bd432a37c5833f26350fb689356 /tw/services/files/aerc
parentda5e9d5ee98dfc216eb7e3b1559c09f4bf868bf6 (diff)
Separate home service into PIM, dev env and graphical parts
This means we only instantiate Shepherd and mcron services if we really need them, to avoid annoyance on servers.
Diffstat (limited to 'tw/services/files/aerc')
-rw-r--r--tw/services/files/aerc/accounts.conf97
-rw-r--r--tw/services/files/aerc/accounts.work.conf26
-rw-r--r--tw/services/files/aerc/aerc.conf157
-rw-r--r--tw/services/files/aerc/binds.conf194
-rwxr-xr-xtw/services/files/aerc/filters/colorize.ansi134
5 files changed, 608 insertions, 0 deletions
diff --git a/tw/services/files/aerc/accounts.conf b/tw/services/files/aerc/accounts.conf
new file mode 100644
index 00000000..5325ee03
--- /dev/null
+++ b/tw/services/files/aerc/accounts.conf
@@ -0,0 +1,97 @@
+# Note: when adding/removing/editing an account name here, edit
+# [messages:account=] and [messages:folder=] sections in binds.conf to match!
+
+[Mythic]
+source = imaps://timo%40twilken.net@oncilla.mythic-beasts.com
+outgoing = smtps+plain://timo%40twilken.net@smtp-auth.mythic-beasts.com
+source-cred-cmd = pass www/mythic-beasts/email/timo | head -1
+outgoing-cred-cmd = pass www/mythic-beasts/email/timo | head -1
+default = INBOX
+from = Timo Wilken <timo@twilken.net>
+archive = Archive
+copy-to = Sent
+postpone = Drafts
+folders-sort = INBOX,Archive,Sent,Drafts
+cache-headers = true
+pgp-auto-sign = true
+pgp-key-id = 53EC3C06856883DD92355BC22FC78504681F69B0
+pgp-opportunistic-encrypt = true
+
+[CERN]
+# https://man.sr.ht/~rjarry/aerc/providers/microsoft.md#office365-with-xoauth2
+source = imaps+xoauth2://timo.wilken%40cern.ch@outlook.office365.com?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+outgoing = smtp+xoauth2://timo.wilken%40cern.ch@smtp.office365.com:587?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+# To authorize for the first time (to get refresh token):
+# mutt_oauth2.py ~/.local/share/aerc/cern.tokens --authorize --authflow authcode --provider microsoft \
+# --email timo.wilken@cern.ch --client-id 9e5f94bc-e8a4-4e73-b8be-63364c29d753 --client-secret '' \
+# --encryption-pipe 'gpg --encrypt --recipient timo@twilken.net'
+# Confirm empty client secret. When done, copy-paste ?code= value from final URL to the command-line.
+# Then, to store the refresh token:
+# gpg --decrypt ~/.local/share/aerc/cern.tokens | jq -r .refresh_token | pass insert -e -f cern/exol/refresh-token
+source-cred-cmd = pass cern/exol/refresh-token
+outgoing-cred-cmd = pass cern/exol/refresh-token
+default = INBOX
+from = Timo Wilken <timo.wilken@cern.ch>
+aliases = twilken@cern.ch
+archive = Archive
+copy-to = Sent Items
+postpone = Drafts
+folders-sort = INBOX,Archive,Sent Items,Drafts
+folders-exclude = Calendar,~Calendar/.*,Contacts,Conversation History,Journal,Notes,Tasks
+cache-headers = true
+pgp-auto-sign = true
+pgp-key-id = C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C
+# Don't auto-encrypt mail, even if we have the keys of all recipients.
+pgp-opportunistic-encrypt = false
+
+[Gmail]
+source = imaps://timo.21.wilken%40gmail.com@imap.gmail.com
+outgoing = smtps+login://timo.21.wilken%40gmail.com@smtp.gmail.com
+source-cred-cmd = pass www/google/app-passwords/mutt | head -1
+outgoing-cred-cmd = pass www/google/app-passwords/mutt | head -1
+default = INBOX
+from = Timo Wilken <timo.21.wilken@gmail.com>
+archive = [Gmail]/All Mail
+copy-to = [Gmail]/Sent Mail
+postpone = [Gmail]/Drafts
+folders-sort = INBOX,[Gmail]/All Mail,[Gmail]/Sent Mail,[Gmail]/Drafts
+folders-exclude = [Gmail],[Gmail]/Chats,[Gmail]/Important
+cache-headers = true
+trusted-authres = mx.google.com
+pgp-auto-sign = true
+pgp-key-id = 53EC3C06856883DD92355BC22FC78504681F69B0
+pgp-opportunistic-encrypt = true
+
+[Cantab]
+source = imaps+xoauth2://tw466%40cantab.ac.uk@outlook.office365.com?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+outgoing = smtp+xoauth2://tw466%40cantab.ac.uk@smtp-mail.outlook.com:587?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+source-cred-cmd = pass cambridge/exol-refresh-token
+outgoing-cred-cmd = pass cambridge/exol-refresh-token
+default = INBOX
+from = Timo Wilken <tw466@cantab.ac.uk>
+archive = Archive
+copy-to = Sent Items
+postpone = Drafts
+folders-sort = INBOX,Archive,Sent Items,Drafts
+folders-exclude = Calendar,~Calendar/.*,Contacts,Conversation History,Journal,Notes,Tasks
+cache-headers = true
+pgp-auto-sign = true
+pgp-key-id = 53EC3C06856883DD92355BC22FC78504681F69B0
+pgp-opportunistic-encrypt = true
+
+[Outlook]
+source = imaps+xoauth2://timo_wilken%40live.co.uk@outlook.office365.com?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+outgoing = smtp+xoauth2://timo_wilken%40live.co.uk@smtp-mail.outlook.com:587?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+source-cred-cmd = pass www/microsoft/exol-refresh-token
+outgoing-cred-cmd = pass www/microsoft/exol-refresh-token
+default = INBOX
+from = Timo Wilken <timo_wilken@live.co.uk>
+archive = Archive
+copy-to = Sent
+postpone = Drafts
+folders-sort = INBOX,Archive,Sent,Drafts
+folders-exclude = Notes
+cache-headers = true
+pgp-auto-sign = true
+pgp-key-id = 53EC3C06856883DD92355BC22FC78504681F69B0
+pgp-opportunistic-encrypt = true
diff --git a/tw/services/files/aerc/accounts.work.conf b/tw/services/files/aerc/accounts.work.conf
new file mode 100644
index 00000000..5d2fa7c7
--- /dev/null
+++ b/tw/services/files/aerc/accounts.work.conf
@@ -0,0 +1,26 @@
+[CERN]
+# https://man.sr.ht/~rjarry/aerc/providers/microsoft.md#office365-with-xoauth2
+source = imaps+xoauth2://timo.wilken%40cern.ch@outlook.office365.com?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+outgoing = smtp+xoauth2://timo.wilken%40cern.ch@smtp.office365.com:587?client_id=9e5f94bc-e8a4-4e73-b8be-63364c29d753&token_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/token&scope=https://outlook.office.com/SMTP.Send https://outlook.office.com/IMAP.AccessAsUser.All offline_access
+# To authorize for the first time (to get refresh token):
+# mutt_oauth2.py ~/.local/share/aerc/twilken.tokens --authorize --authflow authcode --provider microsoft \
+# --email timo.wilken@cern.ch --client-id 9e5f94bc-e8a4-4e73-b8be-63364c29d753 --client-secret '' \
+# --encryption-pipe 'gpg --encrypt --recipient C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C'
+# Confirm empty client secret. When done, copy-paste ?code= value from final URL to the command-line.
+# Then, to store the refresh token:
+# gpg --decrypt ~/.local/share/aerc/twilken.tokens | jq -r .refresh_token | pass insert -e -f cern/exol/refresh-token
+source-cred-cmd = pass cern/exol/refresh-token
+outgoing-cred-cmd = pass cern/exol/refresh-token
+default = INBOX
+from = Timo Wilken <timo.wilken@cern.ch>
+aliases = twilken@cern.ch
+archive = Archive
+copy-to = Sent Items
+postpone = Drafts
+folders-sort = INBOX,Archive,Sent Items,Drafts
+folders-exclude = Calendar,~Calendar/.*,Contacts,Conversation History,Journal,Notes,Tasks
+cache-headers = true
+pgp-auto-sign = true
+pgp-key-id = C2249BBE5E8761C943A0CFA1B7B3914BF63ACD7C
+# Don't auto-encrypt mail, even if we have the keys of all recipients.
+pgp-opportunistic-encrypt = false
diff --git a/tw/services/files/aerc/aerc.conf b/tw/services/files/aerc/aerc.conf
new file mode 100644
index 00000000..7473bc8b
--- /dev/null
+++ b/tw/services/files/aerc/aerc.conf
@@ -0,0 +1,157 @@
+# aerc main configuration
+
+[general]
+#default-save-path=
+# Allow world-readable accounts.conf. This is fine as I don't store any
+# passwords there, only "pass" commands.
+unsafe-accounts-conf=true
+
+[ui]
+# Describes the format for each row in a mailbox view. This field is compatible
+# with mutt's printf-like syntax.
+index-columns=num>4,flags>4,date<21,peers<17,subject<*
+column-num={{.Number}}
+column-flags={{.Flags | join ""}}
+column-date={{.DateAutoFormat .Date.Local}}
+column-peers={{.Peer | names | join ", "}}
+column-subject={{.ThreadPrefix}}{{.Subject}}
+
+# See time.Time#Format at https://godoc.org/time#Time.Format
+timestamp-format=Mon _2 Jan 2006 15:04
+
+# List of space-separated criteria to sort the messages by, see *sort*
+# command in *aerc*(1) for reference. Prefixing a criterion with "-r "
+# reverses that criterion.
+sort=-r date
+
+threading-enabled=true
+
+next-message-on-delete=false
+
+sidebar-width=24
+
+styleset-name=catppuccin
+pinned-tab-marker='πŸ“Œ'
+# Use box-drawing characters for vertical and horizontal borders.
+border-char-vertical=β”‚
+border-char-horizontal=─
+# Use UTF-8 symbols to indicate PGP status of messages
+icon-unencrypted=
+icon-encrypted=πŸ”’
+icon-signed=βœ”
+icon-signed-encrypted=βœ”πŸ”’
+icon-unknown=✘
+icon-invalid=⚠
+
+# Activates fuzzy search in commands and their arguments: the typed string is
+# searched in the command or option in any position, and need not be
+# consecutive characters in the command or option.
+#fuzzy-complete=false
+
+[statusline]
+status-columns=left<*,centre>=,right>*
+column-left=[{{.Account}}] {{.StatusInfo}}
+column-centre={{.PendingKeys}}
+column-right={{.ContentInfo}} {{.TrayInfo}}
+
+# Defines the mode for displaying the status elements.
+# Options: text, icon
+display-mode=icon
+
+[viewer]
+# Specifies the pager to use when displaying emails. Note that some filters
+# may add ANSI codes to add color to rendered emails, so you may want to use a
+# pager which supports ANSI codes.
+pager='env LESSKEYIN=/dev/null less -iRM'
+
+# If an email offers several versions (multipart), you can configure which
+# mimetype to prefer. For example, this can be used to prefer plaintext over
+# html emails.
+alternatives=text/plain,text/html
+
+# Layout of headers when viewing a message. To display multiple headers in the
+# same row, separate them with a pipe, e.g. "From|To". Rows will be hidden if
+# none of their specified headers are present in the message.
+#header-layout=From|To,Cc|Bcc,Date,Subject
+
+[compose]
+# Default header fields to display when composing a message. To display
+# multiple headers in the same row, separate them with a pipe, e.g. "To|From".
+#header-layout=To|From,Subject
+
+# Specifies the command to be used to tab-complete email addresses. Any
+# occurrence of "%s" in the address-book-cmd will be replaced with what the
+# user has typed so far.
+#
+# The command must output the completions to standard output, one completion
+# per line. Each line must be tab-delimited, with an email address occurring as
+# the first field. Only the email address field is required. The second field,
+# if present, will be treated as the contact name. Additional fields are
+# ignored.
+#
+# This parameter can also be set per account in accounts.conf.
+address-book-cmd=khard email --parsable %s
+
+# Allow to address yourself when replying
+reply-to-self=false
+
+# Warn before sending an email that matches the specified regexp but does not
+# have any attachments. Leave empty to disable this feature.
+# Uses Go's regexp syntax, documented at https://golang.org/s/re2syntax. The
+# "(?im)" flags are set by default (case-insensitive and multi-line).
+no-attachment-warning=^[^>]*(attach|Anhang|angehΓ€ngt)
+
+[filters]
+# Filters allow you to pipe an email body through a shell command to render
+# certain emails differently, e.g. highlighting them with ANSI escape codes.
+#
+# The commands are invoked with sh -c. The following folders are appended to
+# the system $PATH to allow referencing filters from their name only:
+#
+# ${XDG_CONFIG_HOME:-~/.config}/aerc/filters
+# ${XDG_DATA_HOME:-~/.local/share}/aerc/filters
+# $PREFIX/share/aerc/filters
+# /usr/share/aerc/filters
+#
+# The following variables are defined in the filter command environment:
+#
+# AERC_MIME_TYPE the part MIME type/subtype
+# AERC_FILENAME the attachment filename (if any)
+#
+# The first filter which matches the email's mimetype will be used, so order
+# them from most to least specific.
+#
+# You can also match on non-mimetypes, by prefixing with the header to match
+# against (non-case-sensitive) and a comma, e.g. subject,text will match a
+# subject which contains "text". Use header,~regex to match against a regex.
+text/plain=colorize.ansi
+text/calendar=calendar
+message/delivery-status=colorize.ansi
+message/rfc822=colorize.ansi
+#text/html=pandoc -f html -t plain | colorize
+#text/html=html | colorize
+text/html=lynx -display_charset=UTF-8 -force_html -dump -stdin
+#text/*=bat -fP --file-name="$AERC_FILENAME"
+#application/x-sh=bat -fP -l sh
+#image/*=catimg -w $(tput cols) -
+#subject,~Git(hub|lab)=lolcat -f
+#from,thatguywhodoesnothardwraphismessages=fmt -w 72 | colorize
+
+[openers]
+# Openers allow you to specify the command to use for the :open action on a
+# per-MIME-type basis.
+#
+# {} is expanded as the temporary filename to be opened. If it is not
+# encountered in the command, the temporary filename will be appened to the end
+# of the command.
+#
+# Examples:
+# text/html=surf -dfgms
+# text/plain=gvim {} +125
+# message/rfc822=thunderbird
+application/pdf=zathura
+image/*=imv
+
+[hooks]
+# Executed when a new email arrives in the selected folder
+new-email=dunstify -a aerc -i mail-unread "New mail from $AERC_FROM_NAME" "$AERC_SUBJECT"
diff --git a/tw/services/files/aerc/binds.conf b/tw/services/files/aerc/binds.conf
new file mode 100644
index 00000000..0e88b571
--- /dev/null
+++ b/tw/services/files/aerc/binds.conf
@@ -0,0 +1,194 @@
+# Binds are of the form <key sequence> = <command to run>
+# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
+# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
+<C-p> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+<C-t> = :term<Enter>
+? = :help keys<Enter>
+
+[messages]
+q = :quit<Enter>
+
+j = :next<Enter>
+<Down> = :next<Enter>
+<C-d> = :next 50%<Enter>
+<C-f> = :next 100%<Enter>
+<PgDn> = :next 100%<Enter>
+
+k = :prev<Enter>
+<Up> = :prev<Enter>
+<C-u> = :prev 50%<Enter>
+<C-b> = :prev 100%<Enter>
+<PgUp> = :prev 100%<Enter>
+g = :select 0<Enter>
+G = :select -1<Enter>
+"#" = :select<space>
+
+J = :next-folder<Enter>
+K = :prev-folder<Enter>
+H = :collapse-folder<Enter>
+L = :expand-folder<Enter>
+
+"*" = :mark -a<Enter>
+v = :mark -t<Enter>
+V = :mark -T<Enter>
+F = :flag -tx Flagged<Enter>
+<C-r> = :read -t<Enter>
+<semicolon> = :remark<Enter>
+
+T = :toggle-threads<Enter>
+
+<Enter> = :view<Enter>
+A = :archive flat<Enter>
+p = :split 25<Enter>
+e = :envelope<Enter>
+
+m = :compose<Enter>
+M = :mkdir<space>
+
+f = :forward -F<Enter>
+a = :reply -aq<Enter>
+r = :reply -q<Enter>
+
+C = :copy<space>
+S = :save<space>
+s = :move<space>
+E = :export-mbox<space>
+I = :import-mbox<space>
+
+c = :cf<space>
+$ = :check-mail<Enter>
+! = :term<space>
+| = :pipe<space>
+
+/ = :search<space>
+l = :filter<space>
+n = :next-result<Enter>
+N = :prev-result<Enter>
+<Esc> = :clear<Enter>:unmark -a<Enter>
+
+[messages:folder=Drafts]
+<Enter> = :recall<Enter>
+
+[view:account=Mythic]
+D = :move Rubbish<Enter>
+[messages:account=Mythic]
+d = :move Rubbish<Enter>
+D = :move Rubbish<Enter>
+# We can't use both :account= and :folder= at the same time, unfortunately.
+[messages:folder=Rubbish]
+d = :delete<Enter>
+D = :delete<Enter>
+
+[view:account=Wilken]
+D = :move Rubbish<Enter>
+[messages:account=Wilken]
+d = :move Rubbish<Enter>
+D = :move Rubbish<Enter>
+[messages:folder=Rubbish]
+d = :delete<Enter>
+D = :delete<Enter>
+
+[view:account=CERN]
+D = :move Deleted Items<Enter>
+[messages:account=CERN]
+d = :move Deleted Items<Enter>
+D = :move Deleted Items<Enter>
+[messages:folder=Deleted Items]
+d = :delete<Enter>
+D = :delete<Enter>
+
+[view:account=Gmail]
+D = :move [Gmail]/Bin<Enter>
+[messages:account=Gmail]
+d = :move [Gmail]/Bin<Enter>
+D = :move [Gmail]/Bin<Enter>
+[messages:folder=[Gmail]/Bin]
+d = :delete<Enter>
+D = :delete<Enter>
+[messages:folder=[Gmail]/Drafts]
+<Enter> = :recall<Enter>
+
+[view:account=Cantab]
+D = :move Deleted Items<Enter>
+[messages:account=Cantab]
+d = :move Deleted Items<Enter>
+D = :move Deleted Items<Enter>
+[messages:folder=Deleted Items]
+d = :delete<Enter>
+D = :delete<Enter>
+
+[view:account=Outlook]
+D = :move Deleted<Enter>
+[messages:account=Outlook]
+d = :move Deleted<Enter>
+D = :move Deleted<Enter>
+[messages:folder=Deleted]
+d = :delete<Enter>
+D = :delete<Enter>
+
+[view]
+/ = :toggle-key-passthrough<Enter>/
+q = :close<Enter>
+O = :open<Enter>
+S = :save<space>
+| = :pipe<space>
+A = :archive flat<Enter>
+
+<C-l> = :open-link <space>
+<C-v> = :toggle-key-passthrough<Enter>
+
+f = :forward -F<Enter>
+a = :reply -aq<Enter>
+r = :reply -q<Enter>
+
+H = :toggle-headers<Enter>
+<C-k> = :prev-part<Enter>
+<C-j> = :next-part<Enter>
+J = :next<Enter>
+K = :prev<Enter>
+
+[view::passthrough]
+$noinherit = true
+$ex = <C-x>
+<Esc> = :toggle-key-passthrough<Enter>
+
+[compose]
+# Keybindings used when the embedded terminal is not selected in the compose view.
+$noinherit = true
+$ex = <C-x>
+<C-k> = :prev-field<Enter>
+<C-j> = :next-field<Enter>
+<A-p> = :switch-account -p<Enter>
+<A-n> = :switch-account -n<Enter>
+<tab> = :next-field<Enter>
+<C-p> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+
+[compose::editor]
+# Keybindings used when the embedded terminal is selected in the compose view.
+$noinherit = true
+$ex = <C-x>
+<C-k> = :prev-field<Enter>
+<C-j> = :next-field<Enter>
+<C-p> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
+
+[compose::review]
+# Keybindings used when reviewing a message to be sent
+y = :send<Enter>
+n = :abort<Enter>
+p = :postpone<Enter>
+q = :choose -o d discard abort -o p postpone postpone<Enter>
+e = :edit<Enter>
+a = :attach<space>
+d = :detach<space>
+v = :preview<Enter>
+s = :sign<Enter>
+c = :encrypt<Enter>
+
+[terminal]
+$noinherit = true
+$ex = <C-x>
+<C-p> = :prev-tab<Enter>
+<C-n> = :next-tab<Enter>
diff --git a/tw/services/files/aerc/filters/colorize.ansi b/tw/services/files/aerc/filters/colorize.ansi
new file mode 100755
index 00000000..c21fd804
--- /dev/null
+++ b/tw/services/files/aerc/filters/colorize.ansi
@@ -0,0 +1,134 @@
+#!/usr/bin/env -S awk -f
+# Copyright (c) 2022 Robin Jarry
+# Adapted for standard ANSI colors (for catppuccin theme) by Timo Wilken.
+
+BEGIN {
+ url = "\033[32m" # green
+ header = "\033[35m" # pink
+ signature = "\033[38m" # Surface 2
+ diff_meta = "\033[1;37m" # bold white
+ diff_chunk = "\033[36m" # teal
+ diff_add = "\033[32m" # green
+ diff_del = "\033[31m" # red
+ quote_1 = "\033[38;5;15m" # Subtext 0
+ quote_2 = "\033[37m" # Subtext 1
+ quote_3 = "\033[38m" # Surface 2
+ quote_4 = "\033[30m" # Surface 1
+ quote_x = "\033[30m" # Surface 1
+ bold = "\033[1m"
+ reset = "\033[0m"
+ # state
+ in_diff = 0
+ in_signature = 0
+ in_headers = 0
+ in_body = 0
+ # patterns
+ header_pattern = @/^[A-Z][[:alnum:]-]+:/
+ url_pattern = @/[a-z]{2,6}:\/\/[[:graph:]]+|(mailto:)?[[:alnum:]_\+\.~\/-]*[[:alnum:]_]@[[:lower:]][[:alnum:]\.-]*[[:lower:]]/
+ meta_pattern = @/^(diff --git|(new|deleted) file|similarity index|(rename|copy) (to|from)|index|---|\+\+\+) /
+}
+function color_quote(line) {
+ level = 0
+ quotes = ""
+ while (line ~ /^>/) {
+ level += 1
+ quotes = quotes ">"
+ line = substr(line, 2)
+ while (line ~ /^ /) {
+ quotes = quotes " "
+ line = substr(line, 2)
+ }
+ }
+ if (level == 1) {
+ color = quote_1
+ } else if (level == 2) {
+ color = quote_2
+ } else if (level == 3) {
+ color = quote_3
+ } else if (level == 4) {
+ color = quote_4
+ } else {
+ color = quote_x
+ }
+ if (line ~ meta_pattern) {
+ return color quotes bold line reset
+ } else if (line ~ /^\+/) {
+ return color quotes diff_add line reset
+ } else if (line ~ /^-/) {
+ return color quotes diff_del line reset
+ }
+ gsub(url_pattern, url "&" color, line)
+ return color quotes line reset
+}
+{
+ # Strip carriage returns from line
+ sub(/\r$/, "")
+
+ if (in_diff) {
+ if ($0 ~ /^-- ?$/) {
+ in_diff = 0
+ in_signature = 1
+ $0 = signature $0 reset
+ } else if ($0 ~ /^@@ /) {
+ gsub(/^@@[^@]+@@/, diff_chunk "&" reset)
+ } else if ($0 ~ meta_pattern) {
+ $0 = diff_meta $0 reset
+ } else if ($0 ~ /^\+/) {
+ $0 = diff_add $0 reset
+ } else if ($0 ~ /^-/) {
+ $0 = diff_del $0 reset
+ } else if ($0 !~ /^ / && $0 !~ /^$/) {
+ in_diff = 0
+ in_body = 1
+ if ($0 ~ /^>/) {
+ $0 = color_quote($0)
+ } else {
+ gsub(url_pattern, url "&" reset)
+ }
+ }
+ } else if (in_signature) {
+ gsub(url_pattern, url "&" signature)
+ $0 = signature $0 reset
+ } else if (in_headers) {
+ if ($0 ~ /^$/) {
+ in_headers = 0
+ in_body = 1
+ } else {
+ sub(header_pattern, header "&" reset)
+ gsub(url_pattern, url "&" reset)
+ }
+ } else if (in_body) {
+ if ($0 ~ /^>/) {
+ $0 = color_quote($0)
+ } else if ($0 ~ /^diff --git /) {
+ in_body = 0
+ in_diff = 1
+ $0 = diff_meta $0 reset
+ } else if ($0 ~ /^-- ?$/) {
+ in_body = 0
+ in_signature = 1
+ $0 = signature $0 reset
+ } else {
+ gsub(url_pattern, url "&" reset)
+ }
+ } else if ($0 ~ /^diff --git /) {
+ in_diff = 1
+ $0 = diff_meta $0 reset
+ } else if ($0 ~ /^-- ?$/) {
+ in_signature = 1
+ $0 = signature $0 reset
+ } else if ($0 ~ header_pattern) {
+ in_headers = 1
+ sub(header_pattern, header "&" reset)
+ gsub(url_pattern, url "&" reset)
+ } else {
+ in_body = 1
+ if ($0 ~ /^>/) {
+ $0 = color_quote($0)
+ } else {
+ gsub(url_pattern, url "&" reset)
+ }
+ }
+
+ print
+}