diff --git a/.gnupg/pinentry-dmenu.conf b/.gnupg/pinentry-dmenu.conf new file mode 100644 index 0000000..ca37c36 --- /dev/null +++ b/.gnupg/pinentry-dmenu.conf @@ -0,0 +1,13 @@ +asterisk= "*"; +min_password_length = 24 +prompt = "GnuPG" +font = "FuraCodeNerdFont-13"; +bottom = false; +prompt_fg = "#FFFFFF"; +prompt_bg = "#771818"; +normal_fg = "#FFFFFF"; +normal_bg = "#771818"; +select_fg = "#FFFFFF"; +select_bg = "#181818"; +desc_fg = "#CCCCCC"; +desc_bg = "#771818"; diff --git a/.mutt/colors/codevoid256 b/.mutt/colors/codevoid256 new file mode 100644 index 0000000..d6f861d --- /dev/null +++ b/.mutt/colors/codevoid256 @@ -0,0 +1,65 @@ +# Programcolors (fg/bg) +color attachment green default +color tree cyan default +color error cyan default +color normal white default +color indicator white color15 +color signature cyan default +color status white brightyellow +color tilde green default +color message cyan default # info messages +color search brightred green # search matches +color markers red default # + at beginning of +color underline brightred default # hilighting underlined + +# Headercolors +color hdrdefault green default +color header yellow default ^From: +color header yellow default ^Cc: +color header brightyellow default ^X +color header green default ^Subject: +color header blue default ^Organization: +color header blue default ^Reply +color header yellow default ^From +color header brightcyan default ^X-Mailer: +color header yellow default ^To: +color header brightyellow default ^X-Mailing-List: +color header brightyellow default ^OpenPGP: +color header brightyellow default ^OpenGPG: +color header brightyellow default ^X-OpenGPG: +color header red default ^Date: +color header brightyellow default ^User-Agent: +color header brightyellow default ^List-Id: +color header brightyellow default ^Sender: +color header brightyellow default ^X-PGP-Fingerprint: + +# Quoting levels +color quoted green default +color quoted1 cyan default +color quoted2 brightblue default +color quoted3 yellow default +color quoted4 green default +color quoted5 cyan default +color quoted6 brightblue default +color quoted7 yellow default + +# Smilies etc. +#color body red default "<[Gg]>" +#color body red default "<[Bb][Gg]>" +#color body red default " [;:]-*[)>(<|]" + +# Emails +color body brightred default "[-a-z_0-9.%$]+@[-a-z_0-9.]+\\.[-a-z][-a-z]+" + +# URLs +color body green default "(https|http|ftp|news|telnet|finger|gopher)://[^ ]*" +color body green default "mailto:[-a-z_0-9.\+]+@[-a-z_0-9.]+" + +# Message types +color index brightyellow default '~P' # from me +#color index cyan default '~p' # personal +color index green default '~G' # PGP +color index red default '~F' # flagged +#color index green default '~Q' # replied +#color index brightwhite default '~N' # new +#color index red default '~D' # deleted diff --git a/.mutt/defaultfolders b/.mutt/defaultfolders new file mode 100644 index 0000000..00f797b --- /dev/null +++ b/.mutt/defaultfolders @@ -0,0 +1,3 @@ +mailboxes \ + "imaps://mail.codevoid.de" \ + "~/.emails" diff --git a/.mutt/mailcap b/.mutt/mailcap new file mode 100644 index 0000000..3690cee --- /dev/null +++ b/.mutt/mailcap @@ -0,0 +1,52 @@ +# USAGE: +# Tyle: text/plain +# Command: cmd %s +# Flags: +# - copiousoutput use pager +# - needsterminal open in new terminal +# - text=xxx rule is valid if program xxx returns true +# - textualnewlines=0 turn off newline conversation for text +# - compose=xxx %s use program xxx to compose this type +# - edit=xxx use program xxx to edit this type +# - description=xxx set description +# - nametemplate=%s.html define temporary file name +# Format: +# - %s - Filename (if %s is not used, data will be sent to stdin) +# - %t - Mime Type +# - %{var1} - Specified by: var1=foobar; +# +# Env Variables: +# METAMAIL_PAGER=less +# +# Tests: +# test -n "$DISPLAY" true when X is running + + +# html +text/html; w3m -I %{charset} -T %t -cols "$COLUMNS" -s -no-graph -o display_link=1 -o display_link_number=1; copiousoutput; + +# documents +application/pdf; zathura %s; nametemplate=%s.pdf; +application/vnd.openxmlformats-officedocument.wordprocessingml.document; libreoffice %s; +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; libreoffice %s; +application/vnd.openxmlformats-officedocument.presentationml.presentation; libreoffice %s; +application/vnd.oasis.opendocument.text; libreoffice %s; +application/vnd.oasis.opendocument.spreadsheet; libreoffice %s; +application/vnd.oasis.opendocument.presentation; libreoffice %s; + +# media +image/*; nsxiv %s; needsterminal; +video/*; mpv %s; needsterminal; +audio/*; mpv %s; needsterminal; +application/ogg; mpv %s; needsterminal; + +# calendar +text/calendar; clear && ~/.mutt/scripts/mutt-ical.py -i -e "stefan.hagen@sap.com" %s; nametemplate=%s.ics +application/ics; clear && ~/.mutt/scripts/mutt-ical.py -i -e "stefan.hagen@sap.com" %s; nametemplate=%s.ics + +# winmail.dat fix +application/ms-tnef; tnef -w %s + +# fallback +application/*; vim %s; needsterminal; +#text/*; cat %s; copiousoutput; diff --git a/.mutt/rc-account-private b/.mutt/rc-account-private new file mode 100644 index 0000000..435c590 --- /dev/null +++ b/.mutt/rc-account-private @@ -0,0 +1,42 @@ +source ~/.mutt/rc-common + +set from="sh@codevoid.de" +set realname="Stefan Hagen" + +unmy_hdr * +my_hdr Precedence: first-class +my_hdr Priority: normal +my_hdr X-Editor: `vim --version | head -1 | tr 'IM' 'im' | awk '{print $1" "$5 }'` +my_hdr X-Operating-System: `uname -rms` +my_hdr X-Mailer: `mutt -v | head -1` +my_hdr X-PGP-Fingerprint: CBD3 C468 64B4 6517 E8FB B90F B6BC 2EC5 52BE 43BA +my_hdr OpenPGP: id=52BE43BA\; url=https://codevoid.de/0/gpg\; preference=signencrypt + +set text_flowed = no +set indent_string = "> " + +set imap_delim_chars='/' +set spoolfile = imaps://mail.codevoid.de/INBOX +set folder = imaps://mail.codevoid.de +set postponed = imaps://mail.codevoid.de/Drafts +set record = imaps://mail.codevoid.de/Sent +set trash = imaps://mail.codevoid.de/Trash +set realname = "Stefan Hagen" + +source "pass Local/mutt-private |" + +set signature = "$HOME/.mutt/signature" +set sendmail = "msmtp -a private" + +set ssl_starttls = yes +set ssl_force_tls = yes +set mail_check = 120 +set timeout = 15 + +macro index " =INBOX" "INBOX" +macro index " =Mailboxes/codevoid.de/sh+openbsd-ports" "ports" +macro index " =Mailboxes/codevoid.de/sh+openbsd-tech" "tech" +macro index " =Mailboxes/codevoid.de/sh+openbsd-hackers" "hackers" +macro index " =Mailboxes/codevoid.de/sh+openbsd-ports-cvs" "ports-cvs" + +mailboxes -nopoll -label Codevoid "imaps://mail.codevoid.de" diff --git a/.mutt/rc-common b/.mutt/rc-common new file mode 100644 index 0000000..482526a --- /dev/null +++ b/.mutt/rc-common @@ -0,0 +1,319 @@ +# CACHE +set mailcap_path = "$HOME/.mutt/mailcap" +set header_cache = "$HOME/.mutt/cache/" +set message_cachedir = "$HOME/.mutt/cache/" +set header_cache_compress = yes # faster without +set message_cache_clean = yes # delete obsolete entries from cache (for outside changes) +set maildir_header_cache_verify = yes # check for changes from outside mutt +set maildir_check_cur = yes # check curr folder for new messages +set mail_check_stats = yes # calculate message statistics +set mail_check_stats_interval = 60 # stat calculation interval + +# set xterm title +set ts_enabled = no + +# IMAP FEATURES +set imap_condstore = yes +set imap_qresync = yes +set imap_deflate = yes +set imap_idle = no +set imap_keepalive = 180 +set imap_fetch_chunk_size = 250 +set imap_pipeline_depth = 0 +set imap_poll_timeout = 30 +set imap_check_subscribed = no +set imap_passive = yes +set mail_check = 360 +set timeout = 5 + +set tmpdir = ~/.cache/mutt + +alternates @codevoid.de|@textmail.me|@stefanhagen-fotografie.de|ptrace.org|sh@uugrn.org|stefan.hagen@uugrn.org|sdk@uugrn.org|sdk@openbsd.org + +# Allow alt key +set meta_key = no + +# Charset Settings +set charset = "utf-8" # terminal +set send_charset = "us-ascii:utf-8" +set config_charset = "utf-8" +set attach_charset = "utf-8" # attachment view +set assumed_charset = "utf-8" +set use_8bitmime = no # only for weird sendmails +set allow_ansi = no # no escape characters please +set allow_8bit = yes + +unlists * +lists misc ports tech hackers uugrn intern vorstand + +# Display Header +ignore * +unignore To Cc Subject Reply-To Mail-Followup-To Message-ID +#unignore From To Cc Subject Reply-To +#unignore Date Sender List-Unsubscribe +#unignore Message-ID +unignore X-Editor X-Mailer X-GPG X-PGP +unignore X-Gnupg PGP GPG Gnupg +unignore X-Spam: + +set imap_headers = "X-Spam X-Spam-DNSBL X-Label" +spam "X-Spam: Yes" "spam" + +hdr_order Date From To Cc Subject Reply-To +hdr_order Sender List-Unsubscribe +hdr_order X-Editor X-Mailer X-GPG X-PGP +hdr_order X-Gnupg PGP GPG Gnupg + +#set text_flowed +set sleep_time = 0 + +set attribution = "%n wrote (%D):" + +# TOFU filter +# set my_tprot='t-prot -aelmtS -c --bigq -Mmutt --spass --pgp-move --pgp-short' +# set display_filter=$my_tprot +set display_filter=~/.mutt/scripts/display-filter + +subscribe ports@openbsd.org +send-hook '~C ^ports@openbsd\.org$' 'my_hdr From: sh+openbsd-ports@codevoid.de' +send-hook '~C ^ports@openbsd\.org$' 'unset signature' + +subscribe misc@openbsd.org +send-hook '~C ^misc@openbsd\.org$' 'my_hdr From: sh+openbsd-misc@codevoid.de' +send-hook '~C ^misc@openbsd\.org$' 'unset signature' + +subscribe tech@openbsd.org +send-hook '~C ^tech@openbsd\.org$' 'my_hdr From: sh+openbsd-tech@codevoid.de' +send-hook '~C ^tech@openbsd\.org$' 'unset signature' + +subscribe hackers@openbsd.org +send-hook '~C ^hackers@openbsd\.org$' 'my_hdr From: sh+openbsd-hackers@codevoid.de' +send-hook '~C ^hackers@openbsd\.org$' 'unset signature' + +subscribe uugrn@uugrn.org +send-hook '~C ^uugrn@uugrn\.org$' 'my_hdr From: sh@uugrn.org' +send-hook '~C ^uugrn@uugrn\.org$' 'unset signature' + +subscribe vorstand@uugrn.org +send-hook '~C ^vorstand@uugrn\.org$' 'my_hdr From: sh@uugrn.org' +send-hook '~C ^vorstand@uugrn\.org$' 'unset signature' + +subscribe mutt-users@mutt.org +send-hook '~C ^mutt-users@mutt\.org$' 'my_hdr From: sh+mutt-users@codevoid.de' +send-hook '~C ^mutt-users@mutt\.org$' 'unset signature' + +subscribe intern@lists.ccc.de +send-hook '~C ^intern@lists.ccc\.de$' 'my_hdr From: Stefan Hagen ' +send-hook '~C ^intern@lists.ccc\.de$' 'unset signature' + +# account hooks +send2-hook '~f ^.*@uugrn.org' 'set sendmail = "msmtp -a uugrn"' +send2-hook '~f ^.*@codevoid.de' 'set sendmail = "msmtp -a private"' +send2-hook '~f ^.*@textmail.me' 'set sendmail = "msmtp -a private"' +send2-hook '~f ^.*@ptrace.org' 'set sendmail = "msmtp -a private"' +send2-hook '~f ^.*@codevoid.de' 'set sendmail = "msmtp -a private"' +send2-hook '~f ^.*@mailbox.org' 'set sendmail = "msmtp -a mboxorg"' +send2-hook '~f ^.*@mailbox.org' 'set sendmail = "msmtp -a mboxorg"' + +set help = no + +# Complete address via +set use_from = yes +set use_envelope_from = yes +set query_command = "mu cfind --format=mutt-ab '%s'" +macro index S "~/.mutt/scripts/mu-find.sh~/.cache/mu/results" "mu search" +bind editor complete-query +bind editor ^T complete + +# Format +set pager_format = "From: %f %* %d %P" +set date_format = "%Y-%m-%d %H:%M %Z" +set index_format = "%Z : %-18.18F : %s %* \ %?y?[%y] ?%[%H:%M %d.%m.%y]" +set forward_format = "Fw: %s" +set attach_format = "%u%D%I %t%4n %6T Size: %s, Type: %m/%M %d %F %> [ %C %e ] " +set folder_format = "%t %2C %d %f %> %s Bytes " +set status_format = '%f All:%m New:%u Mark:%t Del:%d %?V?Limit:%V/%ML? %> %P%' + +# Composing +set editor = "vim -c 'set syntax=mail ft=mail'" # set mail editor +set print_command = "muttprint" +#set editor = "nvi" +set fast_reply = yes # do not ask for subject etc. +set autoedit = no # go directly to the editor. send_to has to be entered to hdr directly +set askcc = no # do not ask for cc address +set askbcc = no # do not ask for cc address +set reply_self = yes # strip own address from reply addresses +set ignore_list_reply_to = no # ignore reply_to set by mailing lists (use the list-reply then) +set bounce_delivered = no # include Delivered-To headers when bouncing messages +set reverse_name = yes # use the recieving address as From address +set edit_headers = yes # show headers in editor +set header = no # insert header into reply text + +# Sending +set mime_forward = ask-no # forward as mime or text? +set reply_to = ask-yes # Reply to reply-to? +set include = yes # include message in replies +set fcc_clear = yes # save message unencrypted. (security issue!) +set hidden_host = no # skip the first part of $hostname (does not affect msg ids) +set save_address = no # take senders full name as default for saving the message +set save_empty = yes # delete mbox if empty (does not work with maildir/imap etc) +set save_name = no # mutt searches for a mailbox with the senders name and saves the mail there instead of record. +set encode_from = yes # quoted-printable if line contains "From ". Avoids address trash +set quote_regexp = '^([ \t]*[|>}])+' +set forward_decode = yes # decode complex mails to text/plain when forwarding +set forward_decrypt = yes # strip pgp +set forward_quote = no # format forwarded message text like a reply, with quote string etc. +set followup_to = yes # generates follow up header if replying to a list +set honor_followup_to = yes # take Mail-Followup-To header into account, whean group-replying + +# File stuff +set mask = "." # show also dotfiles in file browser +set move = no # do not move mail from spool to mbox +set copy = yes # copy the sent messages to $record etc. +set fcc_attach = yes # save attachments in $record etc. + +# Layout +set pager_index_lines = 0 # show a few lines from index above msg +set pager_stop = yes # pgdown does not wrap to next message +set markers = no # Don't add "+" on wrapped lines (hard to copy) +set smart_wrap = yes # wrap entire words +set sort = threads # sorting the mails in threads +set duplicate_threads = yes # groups messages with identical message id +set sort_aux = last-date-received # sorting the threads +set abort_nosubject = yes # abort if message has no subject +set reverse_alias = yes # use username in index instead of email address (if available) +set status_on_top = yes # moves the bottom statusbar to the top +set mark_old = no # mark old unread messages with an o +set arrow_cursor = no # use arrow curser instead of a colored line +set tilde = no # show ~ if mail ends and theres still space on the screen +set menu_scroll = yes # scroll the screen instead of using pages +set sig_dashes = no # set signature dashes "-- " +set sig_on_top = no +set ascii_chars = no # allow only ASCII chars for UI building + +# Thread handling +set strict_threads = yes # thread only by In-Reply-To/References or by Subject. +set sort_re = no # use subject for thread building +set collapse_unread = no # do not hide unread messages in compressed threads +set uncollapse_jump = no # jump to the first unread message after expanding a thread + +# Attachments +set implicit_autoview = yes # discover viewing app via mailcap entry +set attach_split = yes # process attachments one by one. this is for saving, printing, piping. +set mailcap_sanitize = yes # !DO NOT CHANGE! it checks mailcap for bad characters + +unalternative_order * +alternative_order text/plain text/enriched text/html +auto_view text/html text/enriched text/calendar + +# Preview HTML +macro pager  "cat - > /tmp/mutt.html && luakit /tmp/mutt.html" + +#macro pager  "~/.mutt/scripts/search-msgid.sh" + +macro pager,attach  "cat > /tmp/muttpatch.diff~/.mutt/scripts/portpatch2.sh /tmp/muttpatch.diff" + +# Save Patch +#macro pager  "rm -f /tmp/mutt-patch.diff/tmp/mutt-patch.diffecho 'Saved as /tmp/mutt-patch.diff'~/.mutt/scripts/portpatch.sh /tmp/mutt-patch.diff" + +# pipe-message +set pipe_decode_weed = no +set pipe_decode = yes # when piping via pipe-message command, strip headers and decode +set pipe_split = yes # if several msgs are tagged, do the pipe-message command for each +set prompt_after = no # promt if external pager exits +set wait_key = no # wait for a key-press after performing shell/external commands +set beep_new = no # beep if new message arrives +set check_new = no # check for new mails, while the mailbox is open +set auto_tag = yes # function will applied to all tagged messages in the index +set use_domain = no # do not autoqualify messages without hostname +set read_only = no # open folders in read-only mode +set score = no # use the scoring system +set suspend = no # allow mutt to be suspended +set wrap_search = yes # search the mailbox around + +# push V # show version at startup +push * # go to last entry + +# save to folder +macro index s "?" "move a message to a mailbox" +macro pager s "?" "move a message to a mailbox" + +bind index x sync-mailbox + +# POS1 and END navigation +bind index,browser home first-entry +bind index,browser end last-entry +bind pager top +bind pager bottom + +bind pager k previous-line +bind pager j next-line + +# general navigation +bind index,pager up previous-entry +bind index,pager down next-entry +bind browser up previous-line +bind browser down next-line +bind index,pager d delete-message +bind index,pager u undelete-message + +bind index,pager G group-chat-reply + +bind browser d delete-mailbox +bind browser q exit + +# Index keys +bind index Q quit +bind index q noop +bind index c change-folder +bind index v display-message +bind index ' ' next-page +bind index,pager y edit-label +bind index e edit +bind index $ sort-mailbox +bind index a tag-prefix +macro index M "~NN." "Mark all new as read" +macro index I "!~x.~$" + +# Pager keys +bind pager ' ' next-page +bind pager c mail +bind pager / search +bind pager \n noop +bind pager g group-reply +bind pager h display-toggle-weed + +# Compose keys +bind compose \cx send-message +bind pager,index ,S save-message + +# urlview +macro pager \cb "'urlview'" 'Follow links with urlview' + +# TOFU settings +# macro pager T ":unset display_filter:set display_filter='$my_tprot'" 'TOFU protection' + +# TOFU colors +color body brightmagenta black "^\\[---.*" +color body green black "^#v[-+]" + +# SSL Settings +set ssl_verify_host = no + +# GPG Settings (new style crypto - does not support inline gpg) +set crypt_use_gpgme = yes # use the new gpgme method (disabling cumbersome gpg commands below) +set crypt_replyencrypt = yes # encrypt, if original mail was encrypted +set crypt_replysign = yes # sign, if original mail was signed +set crypt_verify_sig = no # verify sig, if sig is available +set crypt_autosign = no # sign mails per default +set crypt_use_pka = yes # http://www.g10code.de/docs/pka-intro.de.pdf +set crypt_autosmime = no +set crypt_protected_headers_save = yes +set crypt_protected_headers_write = yes +set crypt_protected_headers_subject = "..." +set crypt_opportunistic_encrypt = yes # encrypt when key can be found +set autocrypt = no +set pgp_use_gpg_agent = yes + +source ~/.mutt/colors/codevoid +source ~/.mutt/colors/devcolors diff --git a/.mutt/scripts/MIMEmbellish b/.mutt/scripts/MIMEmbellish new file mode 100755 index 0000000..d288017 --- /dev/null +++ b/.mutt/scripts/MIMEmbellish @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 + +import re +import sys +import email +import shlex +import mimetypes +import subprocess +import os.path +from copy import copy +from hashlib import md5 +from email import charset +from email import encoders +from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from email.mime.nonmultipart import MIMENonMultipart +from os.path import basename, splitext + + +charset.add_charset('utf-8', charset.SHORTEST, '8bit') + +command = os.path.expanduser('~/.mutt/scripts/htmlize.sh ') + +def make_alternative(message, part): + alternative = convert(part, 'html', command) + alternative.set_payload(alternative.get_payload()) + return alternative + +def make_replacement(message, part): + return convert(part, 'plain', command) + +def convert(part, to_subtype, command): + payload = part.get_payload() + if isinstance(payload, str): + payload = payload.encode('utf-8') + else: + payload = part.get_payload(None, True) + if not isinstance(payload, bytes): + payload = payload.encode('utf-8') + process = subprocess.run( + shlex.split(command), + input=payload, stdout=subprocess.PIPE, check=True) + return MIMEText(process.stdout, to_subtype, 'utf-8') + +def with_alternative(parent, part, from_signed, + make_alternative=make_alternative, + make_replacement=None): + try: + alternative = make_alternative(parent or part, from_signed or part) + replacement = (make_replacement(parent or part, part) + if from_signed is None and make_replacement is not None + else part) + except: + return parent or part + envelope = MIMEMultipart('alternative') + if parent is None: + for k, v in part.items(): + if (k.lower() != 'mime-version' + and not k.lower().startswith('content-')): + envelope.add_header(k, v) + del part[k] + envelope.attach(replacement) + envelope.attach(alternative) + if parent is None: + return envelope + payload = parent.get_payload() + payload[payload.index(part)] = envelope + return parent + +def tag_attachments(message): + if message.get_content_type() == 'multipart/mixed': + for part in message.get_payload(): + if (part.get_content_maintype() in ['image'] + and 'Content-ID' not in part): + filename = part.get_param('filename', + header='Content-Disposition') + if isinstance(filename, tuple): + filename = str(filename[2], filename[0] or 'us-ascii') + if filename: + filename = splitext(basename(filename))[0] + if filename: + part.add_header('Content-ID', '<{}>'.format(filename)) + return message + +def attachment_from_file_path(attachment_path): + try: + mime, encoding = mimetypes.guess_type(attachment_path, strict=False) + maintype, subtype = mime.split('/') + with open(attachment_path, 'rb') as payload: + attachment = MIMENonMultipart(maintype, subtype) + attachment.set_payload(payload.read()) + encoders.encode_base64(attachment) + if encoding: + attachment.add_header('Content-Encoding', encoding) + return attachment + except: + return None + + +attachment_path_pattern = re.compile(r'\]\s*\(\s*file://(/[^)]*\S)\s*\)|' + r'\]\s*:\s*file://(/.*\S)\s*$', + re.MULTILINE) + +def link_attachments(payload): + attached = [] + attachments = [] + + def on_match(match): + if match.group(1): + attachment_path = match.group(1) + cid_fmt = '](cid:{})' + else: + attachment_path = match.group(2) + cid_fmt = ']: cid:{}' + attachment_id = md5(attachment_path.encode()).hexdigest() + if attachment_id in attached: + return cid_fmt.format(attachment_id) + attachment = attachment_from_file_path(attachment_path) + if attachment: + attachment.add_header('Content-ID', '<{}>'.format(attachment_id)) + attachments.append(attachment) + attached.append(attachment_id) + return cid_fmt.format(attachment_id) + return match.group() + + return attachments, attachment_path_pattern.sub(on_match, payload) + + +def with_local_attachments(parent, part, from_signed, + link_attachments=link_attachments): + if from_signed is None: + attachments, payload = link_attachments(part.get_payload()) + part.set_payload(payload) + else: + attachments, payload = link_attachments(from_signed.get_payload()) + from_signed = copy(from_signed) + from_signed.set_payload(payload) + if not attachments: + return parent, part, from_signed + if parent is None: + parent = MIMEMultipart('mixed') + for k, v in part.items(): + if (k.lower() != 'mime-version' + and not k.lower().startswith('content-')): + parent.add_header(k, v) + del part[k] + parent.attach(part) + for attachment in attachments: + parent.attach(attachment) + return parent, part, from_signed + + +def is_target(part, target_subtypes): + return (part.get('Content-Disposition', 'inline') == 'inline' + and part.get_content_maintype() == 'text' + and part.get_content_subtype() in target_subtypes) + + +def pick_from_signed(part, target_subtypes): + for from_signed in part.get_payload(): + if is_target(from_signed, target_subtypes): + return from_signed + + +def seek_target(message, target_subtypes=['plain', 'markdown']): + if message.is_multipart(): + if message.get_content_type() == 'multipart/signed': + part = pick_from_signed(message, target_subtypes) + if part is not None: + return None, message, part + elif message.get_content_type() == 'multipart/mixed': + for part in message.get_payload(): + if part.is_multipart(): + if part.get_content_type() == 'multipart/signed': + from_signed = pick_from_signed(part, target_subtypes) + if from_signed is not None: + return message, part, from_signed + elif is_target(part, target_subtypes): + return message, part, None + else: + if is_target(message, target_subtypes): + return None, message, None + return None, None, None + + +def main(): + try: + message = email.message_from_file(sys.stdin) + parent, part, from_signed = seek_target(message) + if (parent, part, from_signed) == (None, None, None): + return message + tag_attachments(message) + print(with_alternative( + *with_local_attachments(parent, part, from_signed))) + except (BrokenPipeError, KeyboardInterrupt): + pass + + +if __name__ == '__main__': + main() diff --git a/.mutt/scripts/display-filter b/.mutt/scripts/display-filter new file mode 100755 index 0000000..91af712 --- /dev/null +++ b/.mutt/scripts/display-filter @@ -0,0 +1,8 @@ +#!/bin/sh + +cat - \ + | sed 's|\[--\(.*unsupported.*\)--\]$|## Attachment:\1|g' \ + | egrep -v '\[-- Attachment #1 --\]$' \ + | egrep -v '\[-- Type.* --\]$' \ + | fgrep -v "WARNING: We have NO indication" \ + | cat -s diff --git a/.mutt/scripts/htmlize.sh b/.mutt/scripts/htmlize.sh new file mode 100755 index 0000000..e0b6ef3 --- /dev/null +++ b/.mutt/scripts/htmlize.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# read stdin +content=$(cat) +content_safe=$(echo "$content"| sed -E 's/^(>+)/\1 /g ; + s/^(>+) /\1 /g ; + s|<|\<|g ; + s|>|\>|g ; + s|^\>\>\>\>\>\>|\>\>\>\>\>\>|g ; + s|^\>\>\>\>\>|\>\>\>\>\>|g ; + s|^\>\>\>\>|\>\>\>\>|g ; + s|^\>\>\>|\>\>\>|g ; + s|^\>\>|\>\>|g ; + s|^\>|\>|g ; + s|^----------.*|&| ; + s|^Sender: |&| ; + s|^Date: |&| ; + s|^To: |&| ; + s|^Cc: |&| ; + s|^Subject: |&| ; + s|^-- $|--\ |g ; + s|^Stefan Hagen$|&|g ; + s|^SAP SE Germany, Walldorf$|&|g') + +# wrap html +echo '' +echo ' ' +echo ' ' +echo ' ' +echo ' ' +echo ' ' +echo ' ' +echo ' ' +echo ' ' +echo '
'
+echo ' '
+echo "$content_safe"
+echo ''
+echo '
' +echo '' +echo '' diff --git a/.mutt/scripts/mu-find.sh b/.mutt/scripts/mu-find.sh new file mode 100755 index 0000000..d6a71b0 --- /dev/null +++ b/.mutt/scripts/mu-find.sh @@ -0,0 +1,5 @@ +#!/bin/ksh +printf "MU Seach: " +read -r term + +mu find --clearlinks --format=links --linksdir=~/.cache/mu/results $term diff --git a/.mutt/scripts/mutt-ical.py b/.mutt/scripts/mutt-ical.py new file mode 100755 index 0000000..193faeb --- /dev/null +++ b/.mutt/scripts/mutt-ical.py @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +""" +This script is meant as a simple way to reply to ical invitations from mutt. +See README for instructions and LICENSE for licensing information. +""" + +from __future__ import with_statement + +__author__="Martin Sander" +__license__="MIT" + + +from tzlocal import get_localzone +import pytz +import vobject +import tempfile, time +import os, sys +import warnings +from datetime import date, datetime +from subprocess import Popen, PIPE +from getopt import gnu_getopt as getopt + +timezone = get_localzone() +mutt="mutt" + +usage=""" +usage: +%s [OPTIONS] -e your@email.address filename.ics +OPTIONS: + -i interactive + -a accept + -d decline + -t tentatively accept + -c mutt_command + (accept is default, last one wins) +""" % sys.argv[0] + +def del_if_present(dic, key): + if dic.has_key(key): + del dic[key] + +def set_accept_state(attendees, state): + for attendee in attendees: + attendee.params['PARTSTAT'] = [unicode(state)] + for i in ["RSVP","ROLE","X-NUM-GUESTS","CUTYPE"]: + del_if_present(attendee.params,i) + return attendees + +def get_accept_decline(): + while True: + sys.stdout.write("\nAccept Invitation? [y/n/t/q]") + ans = sys.stdin.readline() + if ans.lower() == 'y\n': + return 'ACCEPTED' + elif ans.lower() == 'n\n': + return 'DECLINED' + elif ans.lower() == 't\n': + return 'TENTATIVE' + elif ans.lower() == 'q\n': + return '' + +def get_answer(invitation): + # create + ans = vobject.newFromBehavior('vcalendar') + ans.add('method') + ans.method.value = "REPLY" + ans.add('vevent') + + # just copy from invitation + for i in ["uid", "summary", "dtstart", "dtend", "organizer"]: + if invitation.vevent.contents.has_key(i): + ans.vevent.add( invitation.vevent.contents[i][0] ) + + # new timestamp + ans.vevent.add('dtstamp') + ans.vevent.dtstamp.value = datetime.utcnow().replace( + tzinfo = invitation.vevent.dtstamp.value.tzinfo) + return ans + +def write_to_tempfile(ical): + tempdir = tempfile.mkdtemp() + icsfile = tempdir+"/event-reply.ics" + with open(icsfile,"w") as f: + f.write(ical.serialize()) + return icsfile, tempdir + +def get_mutt_command(ical, email_address, accept_decline, icsfile): + accept_decline = accept_decline.capitalize() + if ical.vevent.contents.has_key('organizer'): + if hasattr(ical.vevent.organizer,'EMAIL_param'): + sender = ical.vevent.organizer.EMAIL_param + else: + sender = ical.vevent.organizer.value.split(':')[1] #workaround for MS + else: + sender = "NO SENDER" + summary = ical.vevent.contents['summary'][0].value.encode() + command = [mutt, "-e", "my_hdr From: %s" % email_address, "-a", icsfile, + "-s", "%s: %s" % (accept_decline, summary), "--", sender] + #Uncomment the below line, and move it above the -s line to enable the wrapper + #"-e", 'set sendmail=\'ical_reply_sendmail_wrapper.sh\'', + return command + +def execute(command, mailtext): + process = Popen(command, stdin=PIPE) + process.stdin.write(mailtext) + process.stdin.close() + + result = None + while result is None: + result = process.poll() + time.sleep(.1) + if result != 0: + print "unable to send reply, subprocess exited with\ + exit code %d\nPress return to continue" % result + sys.stdin.readline() + +def openics(invitation_file): + with open(invitation_file) as f: + try: + with warnings.catch_warnings(): #vobject uses deprecated Exception stuff + warnings.simplefilter("ignore") + invitation = vobject.readOne(f, ignoreUnreadable=True) + except AttributeError: + invitation = vobject.readOne(f, ignoreUnreadable=True) + return invitation + +def display(ical): + summary = ical.vevent.contents['summary'][0].value.encode() + if ical.vevent.contents.has_key('organizer'): + if hasattr(ical.vevent.organizer,'EMAIL_param'): + sender = ical.vevent.organizer.EMAIL_param + else: + sender = ical.vevent.organizer.value.split(':')[1] #workaround for MS + else: + sender = "NO SENDER" + if ical.vevent.contents.has_key('description'): + description = ical.vevent.contents['description'][0].value + else: + description = "NO DESCRIPTION" + if ical.vevent.contents.has_key('attendee'): + attendees = ical.vevent.contents['attendee'] + else: + attendees = "" + + sys.stdout.write("Start:\t" + ical.vevent.dtstart.value.astimezone(timezone).strftime('%Y-%m-%d %I:%M %p %Z') + "\n") + sys.stdout.write("End:\t" + ical.vevent.dtend.value.astimezone(timezone).strftime('%Y-%m-%d %I:%M %p %Z') + "\n") + sys.stdout.write("From:\t" + sender + "\n") + sys.stdout.write("Title:\t" + summary + "\n") + sys.stdout.write("To:\t") + for attendee in attendees: + if hasattr(attendee,'EMAIL_param'): + sys.stdout.write(attendee.CN_param + " <" + attendee.EMAIL_param + ">, ") + else: + sys.stdout.write(attendee.CN_param + " <" + attendee.value.split(':')[1] + ">, ") #workaround for MS + sys.stdout.write("\n\n") + sys.stdout.write(description + "\n") + +if __name__=="__main__": + email_address = None + email_addresses = [] + accept_decline = '' + opts, args=getopt(sys.argv[1:],"e:aidtc:") + + if len(args) < 1: + sys.stderr.write(usage) + sys.exit(1) + + invitation = openics(args[0]) + #print(invitation) + display(invitation) + + for opt,arg in opts: + if opt == '-e': + email_addresses = arg.split(',') + if opt == '-i': + accept_decline = get_accept_decline() + if opt == '-a': + accept_decline = 'ACCEPTED' + if opt == '-d': + accept_decline = 'DECLINED' + if opt == '-t': + accept_decline = 'TENTATIVE' + if opt == '-c': + mutt = arg + + if accept_decline == '': + sys.exit(0) + + ans = get_answer(invitation) + + if invitation.vevent.contents.has_key('attendee'): + attendees = invitation.vevent.contents['attendee'] + else: + attendees = "" + set_accept_state(attendees,accept_decline) + ans.vevent.add('attendee') + ans.vevent.attendee_list.pop() + flag = 1 + for attendee in attendees: + if hasattr(attendee,'EMAIL_param'): + if attendee.EMAIL_param in email_addresses: + ans.vevent.attendee_list.append(attendee) + email_address = attendee.EMAIL_param + flag = 0 + else: + if attendee.value.split(':')[1] in email_addresses: + ans.vevent.attendee_list.append(attendee) + email_address = attendee.value.split(':')[1] + flag = 0 + if flag: + sys.stderr.write("Seems like you have not been invited to this event!\n") + sys.exit(1) + + icsfile, tempdir = write_to_tempfile(ans) + + mutt_command = get_mutt_command(ans, email_address, accept_decline, icsfile) + mailtext = "From: %s\n\n%s has %s" % (email_address, email_address, accept_decline.lower()) + execute(mutt_command, mailtext) + + os.remove(icsfile) + os.rmdir(tempdir) diff --git a/.mutt/scripts/portpatch.sh b/.mutt/scripts/portpatch.sh new file mode 100755 index 0000000..8622531 --- /dev/null +++ b/.mutt/scripts/portpatch.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +strip=0 + +clear +printf '\n---------------------------------------------------------------------\n' +egrep '^Index|^RCS|^diff --git|^file +' "$1" | sed 's,/cvs,/usr,g' +printf '---------------------------------------------------------------------\n\n' + +printf "Path for patch [/usr/ports]? " +read _path + +[ -z "$_path" ] && _path=/usr/ports +egrep -q '^diff --git a/' "$1" && strip=1 + +#print "Trying to apply patch" +#qprint -d "$1" "$1.out" + +doas patch -Ep"$strip" -d $_path < "$1" +cd $_path && ksh diff --git a/.mutt/scripts/portpatch2.sh b/.mutt/scripts/portpatch2.sh new file mode 100755 index 0000000..f5b6339 --- /dev/null +++ b/.mutt/scripts/portpatch2.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# needs converters/qprint +# mutt: macro pager,attach ^S "cat > /tmp/muttpatch.diff~/.mutt/scripts/portpatch2.sh /tmp/muttpatch.diff" + +clear +printf '\n---------------------------------------------------------------------\n' +grep -E 'Subject: |^Index|^RCS|^diff --git|^file +|^[-+]{3} ' "${1}" +printf '---------------------------------------------------------------------\n\n' + +printf "Apply patch on path [defaults to /usr/ports]? " +read -r _path + +printf "Fix quoted-printable mangeled patch? [y/N]: " +read -r _qprint + +case ${_qprint} in + [y|Y]) _catcmd="qprint -d"; ;; + *) _catcmd="cat"; ;; +esac + +printf "Strip? [0]: " +read -r _strip + +${_catcmd} "${1}" | doas patch -Ep${_strip:=0} -d ${_path:=/usr/ports} +cd ${_path} && doas su diff --git a/.mutt/scripts/search-msgid.sh b/.mutt/scripts/search-msgid.sh new file mode 100755 index 0000000..5261d4d --- /dev/null +++ b/.mutt/scripts/search-msgid.sh @@ -0,0 +1,2 @@ +#!/bin/sh +luakit "https://marc.info/?i=$(cat - | sed -n 's/^Message-ID: <\(.*\)>/\1/p')" diff --git a/.mutt/scripts/sendmail-codevoid.sh b/.mutt/scripts/sendmail-codevoid.sh new file mode 100755 index 0000000..4f9d9d3 --- /dev/null +++ b/.mutt/scripts/sendmail-codevoid.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ssh sdk@mail.codevoid.de "/usr/sbin/sendmail -F 'Stefan Hagen' -f sh@codevoid.de -t" diff --git a/.mutt/scripts/sendmail-openbsd.sh b/.mutt/scripts/sendmail-openbsd.sh new file mode 100755 index 0000000..659f58a --- /dev/null +++ b/.mutt/scripts/sendmail-openbsd.sh @@ -0,0 +1,2 @@ +#!/bin/sh +ssh sdk@cvs.openbsd.org "/usr/sbin/sendmail -F 'Stefan Hagen' -f sdk@openbsd.org -t" diff --git a/.mutt/scripts/vcal2text.pl b/.mutt/scripts/vcal2text.pl new file mode 100755 index 0000000..0be887b --- /dev/null +++ b/.mutt/scripts/vcal2text.pl @@ -0,0 +1,89 @@ +#!/usr/bin/perl + + +# from: http://dollyfish.net.nz/projects/mutt-filters + +# vcalendar-filter is a simple filter to give plain text representations of vcards +# Copyright (C) 2008 Martyn Smith +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script takes a simple VCALENDAR file as input on STDIN and produces a +# human readable text/plain representation of it on STDOUT +# +# It has been designed for use with mutt's auto_view config option, see the +# README file for more details + +use strict; +use warnings; + +use Data::ICal; +use Text::Autoformat; + +my $body = eval { local $/ = undef; <> }; +my $calendar = Data::ICal->new(data => $body); + +# If parsing failed, try parsing as version 1.0 +$calendar = Data::ICal->new(data => $body, vcal10 => 1) unless $calendar; + +# If parsing failed, give up :-( +unless ( $calendar ) { + print "Unable to parse vcalendar: ", $calendar->error_message, "\n"; + print $body; + exit 1; +} + +foreach my $entry ( @{$calendar->{entries}} ) { + my $properties; + + foreach my $property ( keys %{$entry->properties} ) { + next unless defined $entry->property($property); + $properties->{$property} = join(', ', map { $_->decoded_value } @{$entry->property($property)}); + if ( $property eq 'description' ) { + $properties->{$property} = eval qq{"$properties->{$property}"}; + + $properties->{$property} = autoformat $properties->{$property}, { + all => 1, + left => 15, + }; + $properties->{$property} =~ s/^\s*// if defined $properties->{$property}; + } + elsif ( $property =~ m{ \A dt (?: start | end ) \z }xms ) { + if ( $properties->{$property} =~ m{ (\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d) }xms ) { + $properties->{$property} = "$1-$2-$3 $4:$5"; + } + } + } + + if ( $entry->ical_entry_type eq 'VTIMEZONE' ) { + unless ( defined $properties->{tzid} and $properties->{tzid} =~ m{Pacific/Auckland} ) { + print "Timezone : ", $properties->{tzid}, "\n"; + print "\n"; + } + } + elsif ( $entry->ical_entry_type eq 'VEVENT' ) { + print '-' x 72, "\n"; + foreach my $key ( qw(summary BR description BR location organizer dtstart dtend) ) { + if ( $key eq 'BR' ) { + print "\n"; + next; + } + next unless defined $properties->{$key}; + printf "%-12s: %s\n", ucfirst $key, $properties->{$key}; + } + } + else { + print "WARNING: Unknown entry type: ", $entry->ical_entry_type, "\n"; + } +} diff --git a/.mutt/signature b/.mutt/signature new file mode 100644 index 0000000..e69de29