From 5b4a1e80f53ad1ba79f2096aff4b92f87fb707ef Mon Sep 17 00:00:00 2001
From: Ryan Kavanagh <rak@rak.ac>
Date: Thu, 2 Nov 2023 11:43:25 -0400
Subject: UQAM neomutt config

---
 dot_mutt/accounts/kavanagh.ryan.uqam.ca/folders.rc |  5 +++++
 dot_mutt/accounts/kavanagh.ryan.uqam.ca/format.rc  |  5 +++++
 dot_mutt/accounts/kavanagh.ryan.uqam.ca/main.rc    | 12 ++++++++++++
 dot_mutt/accounts/kavanagh.ryan.uqam.ca/read.rc    |  8 ++++++++
 dot_mutt/accounts/kavanagh.ryan.uqam.ca/send.rc    | 14 ++++++++++++++
 5 files changed, 44 insertions(+)
 create mode 100644 dot_mutt/accounts/kavanagh.ryan.uqam.ca/folders.rc
 create mode 100644 dot_mutt/accounts/kavanagh.ryan.uqam.ca/format.rc
 create mode 100644 dot_mutt/accounts/kavanagh.ryan.uqam.ca/main.rc
 create mode 100644 dot_mutt/accounts/kavanagh.ryan.uqam.ca/read.rc
 create mode 100644 dot_mutt/accounts/kavanagh.ryan.uqam.ca/send.rc

diff --git a/dot_mutt/accounts/kavanagh.ryan.uqam.ca/folders.rc b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/folders.rc
new file mode 100644
index 0000000..2b552ff
--- /dev/null
+++ b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/folders.rc
@@ -0,0 +1,5 @@
+# vim: syntax=muttrc
+
+set mbox = '+Read'
+set postponed = '+Drafts'
+set record = '+Sent Items'
diff --git a/dot_mutt/accounts/kavanagh.ryan.uqam.ca/format.rc b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/format.rc
new file mode 100644
index 0000000..5780c9a
--- /dev/null
+++ b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/format.rc
@@ -0,0 +1,5 @@
+# vim: syntax=muttrc
+
+set pager_format = '-%Z-UQAM: %C/%m: %-20.20n   %s%*  -- (%P)'
+set compose_format = '-- UQAM: Compose [Approx. msg size: %l Atts: %a]%>-'
+set status_format = '-%r-UQAM: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%>-(%P)---'
diff --git a/dot_mutt/accounts/kavanagh.ryan.uqam.ca/main.rc b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/main.rc
new file mode 100644
index 0000000..b1a60e5
--- /dev/null
+++ b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/main.rc
@@ -0,0 +1,12 @@
+# vim: syntax=muttrc
+
+cd ~/.mutt/accounts/kavanagh.ryan.uqam.ca
+
+set folder = 'imaps://kavanagh.ryan@uqam.ca@outlook.office365.com'
+
+source folders.rc
+source read.rc
+source send.rc
+source format.rc
+
+cd
diff --git a/dot_mutt/accounts/kavanagh.ryan.uqam.ca/read.rc b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/read.rc
new file mode 100644
index 0000000..df5409d
--- /dev/null
+++ b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/read.rc
@@ -0,0 +1,8 @@
+# vim: syntax=neomuttrc
+
+set imap_authenticators = 'xoauth2'
+set imap_oauth_refresh_command = '~/bin/mutt_oauth2.py --encryption-pipe="cat" --decryption-pipe="cat" ~/.mutt/accounts/kavanagh.ryan.uqam.ca/xoauth2-token'
+set imap_passive = 'no'
+set imap_user = 'kavanagh.ryan@uqam.ca'
+
+unset imap_pass
diff --git a/dot_mutt/accounts/kavanagh.ryan.uqam.ca/send.rc b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/send.rc
new file mode 100644
index 0000000..def78d5
--- /dev/null
+++ b/dot_mutt/accounts/kavanagh.ryan.uqam.ca/send.rc
@@ -0,0 +1,14 @@
+# vim: syntax=muttrc
+
+set copy = yes
+set from = 'Ryan Kavanagh <kavanagh.ryan@uqam.ca>'
+set smtp_authenticators = 'xoauth2'
+set smtp_oauth_refresh_command = '~/bin/mutt_oauth2.py --encryption-pipe="cat" --decryption-pipe="cat" ~/.mutt/accounts/kavanagh.ryan.uqam.ca/xoauth2-token'
+set smtp_url = 'smtp://kavanagh.ryan@uqam.ca@smtp.office365.com:587/'
+
+set attribution = "Le %d, %n a écrit:"
+set attribution_locale = "fr_CA.utf8"
+set date_format = "%a %d %b %Y à %I:%M:%S%p %Z" 
+
+unset sendmail
+unset smtp_pass
-- 
cgit v1.2.3


From 526c30ea4e6924c1568e739b025b18beeabde2f4 Mon Sep 17 00:00:00 2001
From: Ryan Kavanagh <rak@rak.ac>
Date: Wed, 15 Nov 2023 10:48:59 -0500
Subject: update mutt_oauth2.py

---
 bin/executable_mutt_oauth2.py | 57 +++++++++++++++++++++++++++++--------------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/bin/executable_mutt_oauth2.py b/bin/executable_mutt_oauth2.py
index f67364e..b32fc11 100755
--- a/bin/executable_mutt_oauth2.py
+++ b/bin/executable_mutt_oauth2.py
@@ -35,6 +35,7 @@ import hashlib
 import time
 from datetime import timedelta, datetime
 from pathlib import Path
+import shlex
 import socket
 import http.server
 import subprocess
@@ -44,8 +45,8 @@ import subprocess
 # encryption and decryption pipes you prefer. They should read from standard
 # input and write to standard output. The example values here invoke GPG,
 # although won't work until an appropriate identity appears in the first line.
-ENCRYPTION_PIPE = ['cat']
-DECRYPTION_PIPE = ['cat']
+ENCRYPTION_PIPE = ['gpg', '--encrypt', '--recipient', 'YOUR_GPG_IDENTITY']
+DECRYPTION_PIPE = ['gpg', '--decrypt']
 
 registrations = {
     'google': {
@@ -58,8 +59,6 @@ registrations = {
         'smtp_endpoint': 'smtp.gmail.com',
         'sasl_method': 'OAUTHBEARER',
         'scope': 'https://mail.google.com/',
-        'client_id': '',
-        'client_secret': '',
     },
     'microsoft': {
         'authorize_endpoint': 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
@@ -74,17 +73,15 @@ registrations = {
         'scope': ('offline_access https://outlook.office.com/IMAP.AccessAsUser.All '
                   'https://outlook.office.com/POP.AccessAsUser.All '
                   'https://outlook.office.com/SMTP.Send'),
-        'client_id': '08162f7c-0fd2-4200-a84a-f25a4db0b584',
-        'client_secret': 'TxRBilcHdC6WGBee]fs?QR:SJ8nI[g82',
     },
 }
 
 ap = argparse.ArgumentParser(epilog='''
 This script obtains and prints a valid OAuth2 access token.  State is maintained in an
-encrypted TOKENFILE.  Run with "--verbose --authorize" to get started or whenever all
-tokens have expired, optionally with "--authflow" to override the default authorization
-flow.  To truly start over from scratch, first delete TOKENFILE.  Use "--verbose --test"
-to test the IMAP/POP/SMTP endpoints.
+encrypted TOKENFILE.  Run with "--verbose --authorize --encryption-pipe 'foo@bar.org'"
+to get started or whenever all tokens have expired, optionally with "--authflow" to override
+the default authorization flow.  To truly start over from scratch, first delete TOKENFILE.
+Use "--verbose --test" to test the IMAP/POP/SMTP endpoints.
 ''')
 ap.add_argument('-v', '--verbose', action='store_true', help='increase verbosity')
 ap.add_argument('-d', '--debug', action='store_true', help='enable debug output')
@@ -92,8 +89,26 @@ ap.add_argument('tokenfile', help='persistent token storage')
 ap.add_argument('-a', '--authorize', action='store_true', help='manually authorize new tokens')
 ap.add_argument('--authflow', help='authcode | localhostauthcode | devicecode')
 ap.add_argument('-t', '--test', action='store_true', help='test IMAP/POP/SMTP endpoints')
+ap.add_argument('--decryption-pipe', type=shlex.split, default=DECRYPTION_PIPE,
+                help='decryption command (string), reads from stdin and writes '
+                'to stdout, default: "{}"'.format(
+                    " ".join(DECRYPTION_PIPE)))
+ap.add_argument('--encryption-pipe', type=shlex.split,
+                help='encryption command (string), reads from stdin and writes '
+                'to stdout, suggested: "{}"'.format(
+                    " ".join(ENCRYPTION_PIPE)))
+ap.add_argument('--client-id', type=str, default='',
+                help='Provider id from registration')
+ap.add_argument('--client-secret', type=str, default='',
+                help='(optional) Provider secret from registration')
+ap.add_argument('--provider', type=str, choices=registrations.keys(),
+                help='Specify provider to use.')
+ap.add_argument('--email', type=str, help='Your email address.')
 args = ap.parse_args()
 
+ENCRYPTION_PIPE = args.encryption_pipe
+DECRYPTION_PIPE = args.decryption_pipe
+
 token = {}
 path = Path(args.tokenfile)
 if path.exists():
@@ -125,14 +140,19 @@ if args.debug:
 if not token:
     if not args.authorize:
         sys.exit('You must run script with "--authorize" at least once.')
-    print('Available app and endpoint registrations:', *registrations)
-    token['registration'] = input('OAuth2 registration: ')
-    token['authflow'] = input('Preferred OAuth2 flow ("authcode" or "localhostauthcode" '
-                              'or "devicecode"): ')
-    token['email'] = input('Account e-mail address: ')
+    print('', )
+    token['registration'] = args.provider or input(
+        'Available app and endpoint registrations: {regs}\nOAuth2 registration: '.format(
+            regs=', '.join(registrations.keys())))
+    token['authflow'] = args.authflow or input(
+        'Preferred OAuth2 flow ("authcode" or "localhostauthcode" or "devicecode"): '
+    )
+    token['email'] = args.email or input('Account e-mail address: ')
     token['access_token'] = ''
     token['access_token_expiration'] = ''
     token['refresh_token'] = ''
+    token['client_id'] = args.client_id or input('Client ID: ')
+    token['client_secret'] = args.client_secret or input('Client secret: ')
     writetokenfile()
 
 if token['registration'] not in registrations:
@@ -144,7 +164,7 @@ authflow = token['authflow']
 if args.authflow:
     authflow = args.authflow
 
-baseparams = {'client_id': registration['client_id']}
+baseparams = {'client_id': token['client_id']}
 # Microsoft uses 'tenant' but Google does not
 if 'tenant' in registration:
     baseparams['tenant'] = registration['tenant']
@@ -237,7 +257,7 @@ if args.authorize:
             del p[k]
         p.update({'grant_type': 'authorization_code',
                   'code': authcode,
-                  'client_secret': registration['client_secret'],
+                  'client_secret': token['client_secret'],
                   'code_verifier': verifier})
         try:
             response = urllib.request.urlopen(registration['token_endpoint'],
@@ -320,7 +340,8 @@ if not access_token_valid():
     if not token['refresh_token']:
         sys.exit('ERROR: No refresh token. Run script with "--authorize".')
     p = baseparams.copy()
-    p.update({'client_secret': registration['client_secret'],
+    p.update({'client_id': token['client_id'],
+              'client_secret': token['client_secret'],
               'refresh_token': token['refresh_token'],
               'grant_type': 'refresh_token'})
     try:
-- 
cgit v1.2.3