Skip to content

Commit

Permalink
remove python 'requests' dependency for oauth2
Browse files Browse the repository at this point in the history
  • Loading branch information
d99kris committed Aug 3, 2024
1 parent 5e2471d commit d827df2
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 24 deletions.
17 changes: 7 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,6 @@ Gentoo

sudo make install

**Optional (for OAuth 2.0 support):**

pip3 install -U requests

Getting Started
===============

Expand All @@ -206,11 +202,6 @@ Gmail OAuth 2.0 Setup
Use the setup wizard to set up nmail for the account. Example:

$ nmail -s gmail-oauth2
Cache Encryption Password (optional):
Save password (y/n): y

Cache encryption password is optional, if not specified all local cache
encryption will be disabled.

Note: Refer to [Gmail Prerequisites](#gmail-prerequisites) for enabling
IMAP access and obtaining OAuth 2.0 access.
Expand All @@ -226,6 +217,12 @@ [email protected] with your actual outlook / hotmail address):
Password:
Save password (y/n): y

Outlook OAuth 2.0 Setup
-----------------------
Use the setup wizard to set up nmail for the account. Example:

$ nmail -s outlook-oauth2

Other Email Providers
---------------------
Run nmail once in order for it to automatically generate the default config
Expand Down Expand Up @@ -1211,7 +1208,7 @@ Code Formatting
---------------
Uncrustify is used to maintain consistent source code formatting, example:

uncrustify -c etc/uncrustify.cfg --replace --no-backup src/*.cpp src/*.h
./make.sh src


License
Expand Down
7 changes: 5 additions & 2 deletions src/auth.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// auth.cpp
//
// Copyright (c) 2021-2023 Kristofer Berggren
// Copyright (c) 2021-2024 Kristofer Berggren
// All rights reserved.
//
// nmail is distributed under the MIT license, see LICENSE for details.

#include "auth.h"

#include <cstdlib>
#include <iostream>
#include <string>

#include <sys/time.h>
Expand Down Expand Up @@ -319,7 +320,9 @@ int Auth::PerformAction(const AuthAction p_AuthAction)
: "oauth2 refresh failed (%d): %s",
WEXITSTATUS(status), command.c_str());
std::string output = Util::ReadFile(outPath);
LOG_DUMP(output.c_str());

// stderr is logged to terminal before ui started, otherwise to log file
std::cerr << output << "\n";
}
else if (WIFSIGNALED(status))
{
Expand Down
2 changes: 1 addition & 1 deletion src/nmail.1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
.TH NMAIL "1" "July 2024" "nmail 5.1.5" "User Commands"
.TH NMAIL "1" "August 2024" "nmail 5.1.6" "User Commands"
.SH NAME
nmail \- ncurses mail
.SH SYNOPSIS
Expand Down
89 changes: 79 additions & 10 deletions src/oauth2nmail
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

# oauth2nmail
#
# Copyright (c) 2021-2023 Kristofer Berggren
# Copyright (c) 2021-2024 Kristofer Berggren
# All rights reserved.
#
# nmail is distributed under the MIT license, see LICENSE for details.

from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
import http.client
import json
import os
import requests
import stat
import sys
import threading
import time
import urllib.parse
import webbrowser

version = "1.02"
version = "1.03"
pathServed = ""
providers = {
"gmail-oauth2": {
Expand Down Expand Up @@ -83,13 +83,80 @@ def show_help():
def show_version():
print("oauth2nmail v" + version)
print("")
print("Copyright (c) 2021-2023 Kristofer Berggren")
print("Copyright (c) 2021-2024 Kristofer Berggren")
print("")
print("oauth2nmail is distributed under the MIT license.")
print("")
print("Written by Kristofer Berggren.")


# HttpResponse, get() and post() below constitutes a poor man's 'requests' replacement
class HttpResponse:
def __init__(self, status_code, headers, text):
self.status_code = status_code
self.headers = headers
self.text = text


def get(url, headers=None):
if headers is None:
headers = {}

# Parse the URL
parsed_url = urllib.parse.urlparse(url)
connection = http.client.HTTPSConnection(parsed_url.netloc) if parsed_url.scheme == 'https' else http.client.HTTPConnection(parsed_url.netloc)

# Construct the path with query parameters if any
path = parsed_url.path
if parsed_url.query:
path += '?' + parsed_url.query

# Send the GET request
connection.request("GET", path, headers=headers)

# Get the response
response = connection.getresponse()
response_data = response.read().decode()

# Close the connection
connection.close()

return HttpResponse(response.status, response.getheaders(), response_data)


def post(url, data, headers=None):
if headers is None:
headers = {"Content-Type": "application/json"}

# Parse the URL
parsed_url = urllib.parse.urlparse(url)
connection = http.client.HTTPSConnection(parsed_url.netloc) if parsed_url.scheme == 'https' else http.client.HTTPConnection(parsed_url.netloc)

# Encode the data
if headers.get("Content-Type") == "application/x-www-form-urlencoded":
if isinstance(data, dict):
data = urllib.parse.urlencode(data)
data = data.encode('utf-8')
elif headers.get("Content-Type") == "application/json":
if isinstance(data, dict):
data = json.dumps(data)
data = data.encode('utf-8')
else:
raise ValueError("Unsupported Content-Type")

# Send the POST request
connection.request("POST", parsed_url.path, body=data, headers=headers)

# Get the response
response = connection.getresponse()
response_data = response.read().decode()

# Close the connection
connection.close()

return HttpResponse(response.status, response.getheaders(), response_data)


def url_params(params):
param_list = []
for param in params.items():
Expand Down Expand Up @@ -164,7 +231,7 @@ def generate(provider, client_id, client_secret, token_store):
if "code" not in queryDict:
sys.stderr.write("user did not grant permission\n")
return 3

code = queryDict["code"]

# exchange auth code for mail access and refresh token
Expand All @@ -180,7 +247,7 @@ def generate(provider, client_id, client_secret, token_store):
convUrl = providers[provider]["conv_url"]
convHdr = {'Content-Type': 'application/x-www-form-urlencoded'}
try:
convResponse = requests.post(convUrl, data=convParams, headers=convHdr)
convResponse = post(convUrl, data=convParams, headers=convHdr)
except Exception as e:
sys.stderr.write("token request http post failed " + str(e) + "\n")
return 4
Expand Down Expand Up @@ -212,8 +279,9 @@ def generate(provider, client_id, client_secret, token_store):
convParams["scope"] = providers[provider]["user_scope"]
convParams["requested_token_use"] = "on_behalf_of"
convUrl = providers[provider]["conv_url"]
convHdr = {'Content-Type': 'application/x-www-form-urlencoded'}
try:
convResponse = requests.post(convUrl, data=convParams)
convResponse = post(convUrl, data=convParams, headers=convHdr)
except Exception as e:
sys.stderr.write("user token request http post failed " + str(e) + "\n")
return 4
Expand All @@ -238,7 +306,7 @@ def generate(provider, client_id, client_secret, token_store):
infoUrl = providers[provider]["info_url"]
infoHeaders = {'Authorization': 'Bearer ' + access_token}
try:
infoResponse = requests.get(infoUrl, headers=infoHeaders)
infoResponse = get(infoUrl, headers=infoHeaders)
except Exception as e:
sys.stderr.write("email address get request failed " + str(e) + "\n")
return 4
Expand Down Expand Up @@ -273,7 +341,7 @@ def refresh(provider, client_id, client_secret, token_store):
else:
sys.stderr.write("refresh_token not set in " + token_store + "\n")
return 1

# use refresh code to request new access token
refrParams = {}
refrParams["client_id"] = client_id
Expand All @@ -284,8 +352,9 @@ def refresh(provider, client_id, client_secret, token_store):
refrParams["client_secret"] = client_secret

refrUrl = providers[provider]["refr_url"]
refrHdr = {'Content-Type': 'application/x-www-form-urlencoded'}
try:
refrResponse = requests.post(refrUrl, refrParams)
refrResponse = post(refrUrl, data=refrParams, headers=refrHdr)
except Exception as e:
sys.stderr.write("token refresh http post failed " + str(e) + "\n")
return 4
Expand Down
2 changes: 1 addition & 1 deletion src/version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include "version.h"

#define NMAIL_VERSION "5.1.5"
#define NMAIL_VERSION "5.1.6"

std::string Version::GetBuildOs()
{
Expand Down

0 comments on commit d827df2

Please sign in to comment.