Update 2022-12-08 13:17 OpenBSD/amd64
This commit is contained in:
200
.mutt/scripts/MIMEmbellish
Executable file
200
.mutt/scripts/MIMEmbellish
Executable file
@@ -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()
|
||||
8
.mutt/scripts/display-filter
Executable file
8
.mutt/scripts/display-filter
Executable file
@@ -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
|
||||
40
.mutt/scripts/htmlize.sh
Executable file
40
.mutt/scripts/htmlize.sh
Executable file
@@ -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|^\>\>\>\>\>\>|\>\>\>\>\></span><span style="color: x">\></span>|g ;
|
||||
s|^\>\>\>\>\>|\>\>\>\></span><span style="color: gray">\></span>|g ;
|
||||
s|^\>\>\>\>|\>\>\></span><span style="color: brown">\></span>|g ;
|
||||
s|^\>\>\>|\>\></span><span style="color: green">\></span>|g ;
|
||||
s|^\>\>|\><span style="color: orange">\></span>|g ;
|
||||
s|^\>|<span style="color: blue">\></span>|g ;
|
||||
s|^----------.*|<span style="color: gray">&</span>| ;
|
||||
s|^Sender: |<b>&</b>| ;
|
||||
s|^Date: |<b>&</b>| ;
|
||||
s|^To: |<b>&</b>| ;
|
||||
s|^Cc: |<b>&</b>| ;
|
||||
s|^Subject: |<b>&</b>| ;
|
||||
s|^-- $|</font><font face="Arial" size="0.75" color="gray">--\ |g ;
|
||||
s|^Stefan Hagen$|<b>&</b>|g ;
|
||||
s|^SAP SE Germany, Walldorf$|<b>&</b>|g')
|
||||
|
||||
# wrap html
|
||||
echo '<html>'
|
||||
echo ' <head>'
|
||||
echo ' <meta charset="utf-8">'
|
||||
echo ' <meta name="viewport" content="width=device-width, initial-scale=1">'
|
||||
echo ' <meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
||||
echo ' <meta http-equiv="X-UA-Compatible" content="IE=edge">'
|
||||
echo ' <title></title>'
|
||||
echo ' </head>'
|
||||
echo ' <body style="min-width: 74ch; color: black; font-size: 11; line-height: 1.6; font: 11, \"Calibri\"">'
|
||||
echo ' <pre>'
|
||||
echo ' <font size="3" face="Calibri">'
|
||||
echo "$content_safe"
|
||||
echo '</font>'
|
||||
echo '</pre>'
|
||||
echo '</body>'
|
||||
echo '</html>'
|
||||
5
.mutt/scripts/mu-find.sh
Executable file
5
.mutt/scripts/mu-find.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/ksh
|
||||
printf "MU Seach: "
|
||||
read -r term
|
||||
|
||||
mu find --clearlinks --format=links --linksdir=~/.cache/mu/results $term
|
||||
223
.mutt/scripts/mutt-ical.py
Executable file
223
.mutt/scripts/mutt-ical.py
Executable file
@@ -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)
|
||||
20
.mutt/scripts/portpatch.sh
Executable file
20
.mutt/scripts/portpatch.sh
Executable file
@@ -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
|
||||
26
.mutt/scripts/portpatch2.sh
Executable file
26
.mutt/scripts/portpatch2.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# needs converters/qprint
|
||||
# mutt: macro pager,attach ^S "<pipe-message>cat > /tmp/muttpatch.diff<enter><shell-escape>~/.mutt/scripts/portpatch2.sh /tmp/muttpatch.diff<enter>"
|
||||
|
||||
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
|
||||
2
.mutt/scripts/search-msgid.sh
Executable file
2
.mutt/scripts/search-msgid.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
luakit "https://marc.info/?i=$(cat - | sed -n 's/^Message-ID: <\(.*\)>/\1/p')"
|
||||
2
.mutt/scripts/sendmail-codevoid.sh
Executable file
2
.mutt/scripts/sendmail-codevoid.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ssh sdk@mail.codevoid.de "/usr/sbin/sendmail -F 'Stefan Hagen' -f sh@codevoid.de -t"
|
||||
2
.mutt/scripts/sendmail-openbsd.sh
Executable file
2
.mutt/scripts/sendmail-openbsd.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ssh sdk@cvs.openbsd.org "/usr/sbin/sendmail -F 'Stefan Hagen' -f sdk@openbsd.org -t"
|
||||
89
.mutt/scripts/vcal2text.pl
Executable file
89
.mutt/scripts/vcal2text.pl
Executable file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# 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";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user