diff --git a/ChangeLog b/ChangeLog index 4bf5e85..6a6ee3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ v4.5.0 * Drop support for python <3.10 * Respect locally-installed certificates (ajkessel) + * Re-add a `--noauth_local_server` to provide instructions for authenticating + from a remote system using port forwarding + v4.4.0 * Fix lots of bugs by switching from deprecated oauth2client to google_auth_oauthlib diff --git a/docs/man1/gcalcli.1 b/docs/man1/gcalcli.1 index 30e47a1..7018e4c 100644 --- a/docs/man1/gcalcli.1 +++ b/docs/man1/gcalcli.1 @@ -13,26 +13,27 @@ Additionally, gcalcli can be used as a reminder service and execute any application you want when an event is coming up. .SH USAGE -gcalcli [-h] - [--version] [--default-calendar DEFAULT_CALENDAR] - [--calendar CALENDAR] [--refresh] [--client-id CLIENT_ID] - [--conky] [--nocache] [--locale LOCALE] [--noincluderc] - [--lineart {fancy,unicode,ascii}] - [--config-folder CONFIG_FOLDER] [--nocolor] - [--client-secret CLIENT_SECRET] - {list,search,edit,delete,agenda,calw,calm,quick,add,import,remind} - ... +usage: gcalcli [-h] [--version] [--client-id CLIENT_ID] [--client-secret CLIENT_SECRET] + [--noauth_local_server] [--config-folder CONFIG_FOLDER] [--noincluderc] + [--calendar CALENDAR] [--default-calendar DEFAULTCALENDAR] [--locale LOCALE] [--refresh] + [--nocache] [--conky] [--nocolor] [--lineart {fancy,unicode,ascii}] + {list,search,edit,delete,agenda,agendaupdate,updates,conflicts,calw,calm,quick,add,import,remind} + ... +Google Calendar Command Line Interface -Positional arguments: - {list,search,edit,delete,agenda,updates,conflicts,calw,calm,quick,add,import,remind} - Invoking a subcommand with --help prints subcommand - usage. +positional arguments: + {list,search,edit,delete,agenda,agendaupdate,updates,conflicts,calw,calm,quick,add,import,remind} + Invoking a subcommand with --help prints subcommand usage. list list available calendars + search search for events within an optional time period edit edit calendar events + delete delete events from the calendar agenda get an agenda for a time period - updates get updates since a datetime for a time period - conflicts find conflicts between events matching search term + agendaupdate update calendar from agenda TSV file + updates get updates since a datetime for a time period (defaults to through end of current + month) + conflicts find event conflicts calw get a week-based agenda in calendar format calm get a month agenda in calendar format quick quick-add an event to a calendar @@ -40,33 +41,37 @@ Positional arguments: import import an ics/vcal file to a calendar remind execute command if event occurs within time -Optional arguments: +options: -h, --help show this help message and exit --version show program's version number and exit + --client-id CLIENT_ID + API client_id (default: + 232867676714.apps.googleusercontent.com) + --client-secret CLIENT_SECRET + API client_secret (default: 3tZSxItw6_VnZMezQwC8lUqy) + --noauth_local_server + Provide instructions for authenticating from a remote system using port + forwarding. Note: Previously this option invoked an "Out-Of-Band" variant of the + auth flow, but that deprecated mechanism is no longer supported. (default: True) + --config-folder CONFIG_FOLDER + Optional directory to load/store all configuration + information (default: None) + --noincluderc Whether to include ~/.gcalclirc when using + configFolder (default: True) + --calendar CALENDAR Which calendars to use (default: []) --default-calendar DEFAULT_CALENDAR Optional default calendar to use if no --calendar options are given (default: []) - --calendar CALENDAR Which calendars to use (default: []) + --locale LOCALE System locale (default: ) --refresh Delete and refresh cached data (default: False) - --client-id CLIENT_ID - API client_id (default: - 232867676714.apps.googleusercontent.com) --conky Use Conky color codes (default: False) --nocache Execute command without using cache (default: True) - --locale LOCALE System locale (default: ) - --noincluderc Whether to include ~/.gcalclirc when using - configFolder (default: True) --lineart {fancy,unicode,ascii} Choose line art style for calendars: "fancy": for VTcodes, "unicode" for Unicode box drawing characters, "ascii" for old-school plusses, hyphens and pipes. (default: fancy) - --config-folder CONFIG_FOLDER - Optional directory to load/store all configuration - information (default: None) --nocolor Enable/Disable all color output (default: True) - --client-secret CLIENT_SECRET - API client_secret (default: 3tZSxItw6_VnZMezQwC8lUqy) .SH DIAGNOSTICS diff --git a/gcalcli/argparsers.py b/gcalcli/argparsers.py index b682f5a..425b3c8 100644 --- a/gcalcli/argparsers.py +++ b/gcalcli/argparsers.py @@ -17,49 +17,80 @@ from .printer import valid_color_name PROGRAM_OPTIONS = { - '--client-id': {'default': None, - 'type': str, - 'help': 'API client_id'}, - '--client-secret': {'default': None, - 'type': str, - 'help': 'API client_secret'}, - '--config-folder': {'default': None, 'type': str, - 'help': 'Optional directory to load/store all ' + - 'configuration information'}, - '--noincluderc': {'action': 'store_false', - 'dest': 'includeRc', - 'help': 'Whether to include ~/.gcalclirc when ' + - 'using configFolder'}, - '--calendar': {'default': [], 'type': str, 'action': 'append', - 'help': 'Which calendars to use, in the format ' - '"CalendarName" or "CalendarName#color", where ' - 'the #color suffix is the name of a valid ANSI ' - 'color (such as "brightblue"). This option may ' - 'be called multiple times to display ' - 'additional calendars.'}, - '--default-calendar': {'default': [], 'type': str, 'action': 'append', - 'dest': 'defaultCalendar', - 'help': 'Optional default calendar to use if ' + - 'no --calendar options are given'}, - '--locale': {'default': '', 'type': str, 'help': 'System locale'}, - '--refresh': {'action': 'store_true', 'dest': 'refresh_cache', - 'default': False, - 'help': 'Delete and refresh cached data'}, - '--nocache': {'action': 'store_false', 'dest': 'use_cache', - 'default': True, - 'help': 'Execute command without using cache'}, - '--conky': {'action': 'store_true', 'default': False, - 'help': 'Use Conky color codes'}, - '--nocolor': {'action': 'store_false', 'default': True, - 'dest': 'color', - 'help': 'Enable/Disable all color output'}, - '--lineart': {'default': 'fancy', - 'choices': ['fancy', 'unicode', 'ascii'], - 'help': 'Choose line art style for calendars: ' + - '"fancy": for VTcodes, "unicode" for ' + - 'Unicode box drawing characters, "ascii" ' + - 'for old-school plusses, hyphens and pipes.'}, - } + '--client-id': {'default': None, 'type': str, 'help': 'API client_id'}, + '--client-secret': { + 'default': None, + 'type': str, + 'help': 'API client_secret', + }, + '--noauth_local_server': { + 'action': 'store_false', + 'dest': 'auth_local_server', + 'help': 'Provide instructions for authenticating from a remote system ' + 'using port forwarding.\nNote: Previously this option invoked an ' + '"Out-Of-Band" variant of the auth flow, but that deprecated mechanism ' + 'is no longer supported.', + }, + '--config-folder': { + 'default': None, + 'type': str, + 'help': 'Optional directory to load/store all configuration ' + 'information', + }, + '--noincluderc': { + 'action': 'store_false', + 'dest': 'includeRc', + 'help': 'Whether to include ~/.gcalclirc when using configFolder', + }, + '--calendar': { + 'default': [], + 'type': str, + 'action': 'append', + 'help': 'Which calendars to use, in the format "CalendarName" or ' + '"CalendarName#color", where the #color suffix is the name of a valid ' + 'ANSI color (such as "brightblue"). This option may be called multiple ' + 'times to display additional calendars.', + }, + '--default-calendar': { + 'default': [], + 'type': str, + 'action': 'append', + 'dest': 'defaultCalendar', + 'help': 'Optional default calendar to use if no --calendar options are ' + 'given', + }, + '--locale': {'default': '', 'type': str, 'help': 'System locale'}, + '--refresh': { + 'action': 'store_true', + 'dest': 'refresh_cache', + 'default': False, + 'help': 'Delete and refresh cached data', + }, + '--nocache': { + 'action': 'store_false', + 'dest': 'use_cache', + 'default': True, + 'help': 'Execute command without using cache', + }, + '--conky': { + 'action': 'store_true', + 'default': False, + 'help': 'Use Conky color codes', + }, + '--nocolor': { + 'action': 'store_false', + 'default': True, + 'dest': 'color', + 'help': 'Enable/Disable all color output', + }, + '--lineart': { + 'default': 'fancy', + 'choices': ['fancy', 'unicode', 'ascii'], + 'help': 'Choose line art style for calendars: "fancy": for VTcodes, ' + '"unicode" for Unicode box drawing characters, "ascii" for old-school ' + 'plusses, hyphens and pipes.', + }, +} class DetailsAction(argparse._AppendAction): diff --git a/gcalcli/auth.py b/gcalcli/auth.py index 6a91b1f..f1a947a 100644 --- a/gcalcli/auth.py +++ b/gcalcli/auth.py @@ -3,9 +3,12 @@ from google.auth.transport.requests import Request from google_auth_oauthlib.flow import InstalledAppFlow from google.oauth2.credentials import Credentials +from gcalcli.printer import Printer -def authenticate(client_id: str, client_secret: str): +def authenticate( + client_id: str, client_secret: str, printer: Printer, local: bool +): flow = InstalledAppFlow.from_client_config( client_config={ "installed": { @@ -19,16 +22,38 @@ def authenticate(client_id: str, client_secret: str): }, scopes=["https://www.googleapis.com/auth/calendar"], ) + if not local: + printer.msg( + 'Note: Behavior of the `--noauth-local-server` option has changed! ' + 'Starting local server, but providing instructions for connecting ' + 'to it remotely...\n' + ) credentials = None attempt_num = 0 # Retry up to 5 attempts with different random ports. while credentials is None: port = _free_local_port() + if not local: + printer.msg('Option 1 (outbound):\n', 'yellow') + printer.msg( + ' To establish a connection from this system to a remote ' + 'host, execute a command like: `ssh username@host -L ' + f'{port}:localhost:{port} BROWSER=open $BROWSER ' + "'https://the-url-below'`\n", + ) + printer.msg('Option 2 (outbound):\n', 'yellow') + printer.msg( + ' To establish a connection from a remote host to this ' + 'system, execute a command from remote host like: ' + f'`ssh username@host -fN -R {port}:localhost:{port} ; ' + "BROWSER=open $BROWSER https://the-url-below'`\n\n", + ) try: credentials = flow.run_local_server(open_browser=False, port=port) except OSError as e: if e.errno == 98 and attempt_num < 4: # Will get retried with a different port. + printer.msg(f'Port {port} in use, trying another port...') attempt_num += 1 else: raise diff --git a/gcalcli/gcal.py b/gcalcli/gcal.py index 24fe2fd..0380029 100644 --- a/gcalcli/gcal.py +++ b/gcalcli/gcal.py @@ -196,7 +196,12 @@ def _google_auth(self): 'You will likely see a security warning page and need to ' 'click "Advanced" and "Go to gcalcli (unsafe)" to proceed.\n' ) - self.credentials = auth.authenticate(client_id, client_secret) + self.credentials = auth.authenticate( + client_id, + client_secret, + printer=self.printer, + local=self.options['auth_local_server'], + ) with open(oauth_filepath, 'wb') as gcalcli_oauth: pickle.dump(self.credentials, gcalcli_oauth)