Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/enable-gcp-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,19 @@ If the process was successful, you will see the following screen:

![](images/gmail-api-enabled.png)

The Gmail API is enabled.
The Gmail API is enabled.

## People API (for contacts)

Navigate to this link:
https://console.cloud.google.com/marketplace/product/google/people.googleapis.com

Click to "ENABLE" button.

![](images/people-api-enable.png)

If the process was successful, you will see the following screen:

![](images/people-api-enabled.png)

The People API is enabled.
Binary file added docs/images/people-api-enable.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/people-api-enabled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion gwbackupy/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from gwbackupy import gwbackupy_cli
from gwbackupy.cli import gwbackupy_cli

if __name__ == "__main__":
gwbackupy_cli.cli_startup()
71 changes: 71 additions & 0 deletions gwbackupy/cli/gmail_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
def add_cli_args_gmail(service_parser):
gmail_parser = service_parser.add_parser("gmail", help="GMail service commands")
gmail_command_parser = gmail_parser.add_subparsers(dest="command")

gmail_oauth_init_parser = gmail_command_parser.add_parser(
"access-init", help="Access initialization e.g. OAuth authentication"
)
gmail_oauth_init_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
gmail_oauth_check_parser = gmail_command_parser.add_parser(
"access-check", help="Check access e.g. OAuth tokens"
)
gmail_oauth_check_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
gmail_backup_parser = gmail_command_parser.add_parser("backup", help="Backup gmail")
gmail_backup_parser.add_argument(
"--email", type=str, help="Email of the account", required=True
)
gmail_backup_parser.add_argument(
"--quick-sync-days",
type=int,
default=None,
help="Quick sync number of days back. (It does not delete messages from local "
"storage.)",
)

gmail_restore_parser = gmail_command_parser.add_parser(
"restore", help="Restore gmail"
)
gmail_restore_parser.add_argument(
"--email", type=str, help="Email from which restore", required=True
)
gmail_restore_parser.add_argument(
"--to-email",
type=str,
help="Destination email account, if not specified, then --email is used",
)
gmail_restore_parser.add_argument(
"--add-label",
type=str,
action="append",
help="Add label to restored emails",
default=None,
dest="add_labels",
)
gmail_restore_parser.add_argument(
"--restore-deleted",
help="Restore deleted emails",
default=False,
action="store_true",
)
gmail_restore_parser.add_argument(
"--restore-missing",
help="Restore missing emails",
default=False,
action="store_true",
)
gmail_restore_parser.add_argument(
"--filter-date-from",
type=str,
help="Filter date from (inclusive, format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss)",
default=None,
)
gmail_restore_parser.add_argument(
"--filter-date-to",
type=str,
help="Filter date to (exclusive, format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss)",
default=None,
)
141 changes: 56 additions & 85 deletions gwbackupy/gwbackupy_cli.py → gwbackupy/cli/gwbackupy_cli.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
import argparse
import logging
import os
import sys
import threading

import pytz as pytz
from tzlocal import get_localzone

import gwbackupy.global_properties as global_properties
from gwbackupy.cli.gmail_cli import add_cli_args_gmail
from gwbackupy.cli.peoples_cli import add_cli_args_peoples
from gwbackupy.filters.gmail_filter import GmailFilter
from gwbackupy.gmail import Gmail
from gwbackupy.helpers import parse_date
from gwbackupy.people import People
from gwbackupy.providers.gapi_gmail_service_wrapper import GapiGmailServiceWrapper
from gwbackupy.providers.gapi_people_service_wrapper import GapiPeopleServiceWrapper
from gwbackupy.providers.gapi_service_provider import AccessNotInitializedError
from gwbackupy.providers.gmail_service_provider import GmailServiceProvider
from gwbackupy.providers.people_service_provider import PeopleServiceProvider
from gwbackupy.storage.file_storage import FileStorage

lock = threading.Lock()


def parse_arguments() -> argparse.Namespace:
def parse_arguments(people_cli=None) -> argparse.Namespace:
log_levels = {
"finest": global_properties.log_finest,
"debug": logging.DEBUG,
Expand Down Expand Up @@ -104,77 +110,9 @@ def parse_arguments() -> argparse.Namespace:
help="OAuth redirect host, default is localhost",
)
service_parser = parser.add_subparsers(dest="service")
gmail_parser = service_parser.add_parser("gmail", help="GMail service commands")
gmail_command_parser = gmail_parser.add_subparsers(dest="command")
add_cli_args_gmail(service_parser)
add_cli_args_peoples(service_parser)

gmail_oauth_init_parser = gmail_command_parser.add_parser(
"access-init", help="Access initialization e.g. OAuth authentication"
)
gmail_oauth_init_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
gmail_oauth_check_parser = gmail_command_parser.add_parser(
"access-check", help="Check access e.g. OAuth tokens"
)
gmail_oauth_check_parser.add_argument(
"--email", type=str, help="Email account", required=True
)

gmail_backup_parser = gmail_command_parser.add_parser("backup", help="Backup gmail")
gmail_backup_parser.add_argument(
"--email", type=str, help="Email of the account", required=True
)
gmail_backup_parser.add_argument(
"--quick-sync-days",
type=int,
default=None,
help="Quick sync number of days back. (It does not delete messages from local "
"storage.)",
)

gmail_restore_parser = gmail_command_parser.add_parser(
"restore", help="Restore gmail"
)
gmail_restore_parser.add_argument(
"--email", type=str, help="Email from which restore", required=True
)
gmail_restore_parser.add_argument(
"--to-email",
type=str,
help="Destination email account, if not specified, then --email is used",
)
gmail_restore_parser.add_argument(
"--add-label",
type=str,
action="append",
help="Add label to restored emails",
default=None,
dest="add_labels",
)
gmail_restore_parser.add_argument(
"--restore-deleted",
help="Restore deleted emails",
default=False,
action="store_true",
)
gmail_restore_parser.add_argument(
"--restore-missing",
help="Restore missing emails",
default=False,
action="store_true",
)
gmail_restore_parser.add_argument(
"--filter-date-from",
type=str,
help="Filter date from (inclusive, format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss)",
default=None,
)
gmail_restore_parser.add_argument(
"--filter-date-to",
type=str,
help="Filter date to (exclusive, format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss)",
default=None,
)
if len(sys.argv) == 1 or "--help" in sys.argv:
parser.print_help(sys.stderr)
sys.exit(1)
Expand Down Expand Up @@ -204,23 +142,56 @@ def parse_arguments() -> argparse.Namespace:
def cli_startup():
try:
args = parse_arguments()
if args.service == "gmail":
storage = FileStorage(args.workdir + "/" + args.email + "/gmail")
storage_oauth_tokens = FileStorage(args.workdir + "/oauth-tokens")
service_provider = GmailServiceProvider(
credentials_file_path=args.credentials_filepath,
service_account_email=args.service_account_email,
service_account_file_path=args.service_account_key_filepath,
storage=storage_oauth_tokens,
oauth_bind_addr=args.oauth_bind_address,
oauth_port=args.oauth_port,
oauth_redirect_host=args.oauth_redirect_host,

storage = FileStorage(os.path.join(args.workdir, args.email, args.service))
storage_oauth_tokens = FileStorage(os.path.join(args.workdir, "oauth-tokens"))
service_provider_args = {
"credentials_file_path": args.credentials_filepath,
"service_account_email": args.service_account_email,
"service_account_file_path": args.service_account_key_filepath,
"storage": storage_oauth_tokens,
"oauth_bind_addr": args.oauth_bind_address,
"oauth_port": args.oauth_port,
"oauth_redirect_host": args.oauth_redirect_host,
}

if args.service == "peoples":
service_provider = PeopleServiceProvider(**service_provider_args)
service_wrapper = GapiPeopleServiceWrapper(
service_provider=service_provider,
dry_mode=args.dry,
)

service = People(
email=args.email,
service_wrapper=service_wrapper,
# batch_size=args.batch_size,
batch_size=1,
storage=storage,
dry_mode=args.dry,
)
if args.command == "access-init":
service_wrapper.get_peoples(args.email)
elif args.command == "access-check":
try:
with service_provider.get_service(args.email, False) as s:
service_wrapper.get_peoples(args.email)
except AccessNotInitializedError:
exit(1)
elif args.command == "backup":
if service.backup():
exit(0)
else:
exit(1)
else:
exit(1)
elif args.service == "gmail":
service_provider = GmailServiceProvider(**service_provider_args)
service_wrapper = GapiGmailServiceWrapper(
service_provider=service_provider,
dry_mode=args.dry,
)
gmail = Gmail(
service = Gmail(
email=args.email,
service_wrapper=service_wrapper,
batch_size=args.batch_size,
Expand All @@ -236,7 +207,7 @@ def cli_startup():
except AccessNotInitializedError:
exit(1)
elif args.command == "backup":
if gmail.backup(quick_sync_days=args.quick_sync_days):
if service.backup(quick_sync_days=args.quick_sync_days):
exit(0)
else:
exit(1)
Expand All @@ -258,7 +229,7 @@ def cli_startup():
dt = parse_date(args.filter_date_to, args.timezone)
item_filter.date_to(dt)
logging.info(f"Filter options: date to {dt}")
if gmail.restore(
if service.restore(
to_email=args.to_email,
item_filter=item_filter,
restore_deleted=args.restore_deleted,
Expand Down
56 changes: 56 additions & 0 deletions gwbackupy/cli/peoples_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from gwbackupy.helpers import parse_date


def add_cli_args_peoples(service_parser):
people_parser = service_parser.add_parser(
"peoples", help="Peoples (contacts) service commands"
)
people_command_parser = people_parser.add_subparsers(dest="command")
people_oauth_init_parser = people_command_parser.add_parser(
"access-init", help="Access initialization e.g. OAuth authentication"
)
people_oauth_init_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
people_oauth_check_parser = people_command_parser.add_parser(
"access-check", help="Check access e.g. OAuth tokens"
)
people_oauth_check_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
people_backup_parser = people_command_parser.add_parser(
"backup", help="Backup people"
)
people_backup_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
people_backup_parser.add_argument(
"--start-date",
type=parse_date,
help="Start date (inclusive)",
required=False,
)
people_backup_parser.add_argument(
"--end-date",
type=parse_date,
help="End date (exclusive)",
required=False,
)
people_restore_parser = people_command_parser.add_parser(
"restore", help="Restore people"
)
people_restore_parser.add_argument(
"--email", type=str, help="Email account", required=True
)
people_restore_parser.add_argument(
"--restore-deleted",
help="Restore deleted emails",
default=False,
action="store_true",
)
people_restore_parser.add_argument(
"--restore-missing",
help="Restore missing emails",
default=False,
action="store_true",
)
1 change: 0 additions & 1 deletion gwbackupy/gmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ def __init__(
batch_size = 5
self.batch_size = batch_size
self.__lock = threading.RLock()
self.__services = {}
self.__error_count = 0
self.__service_wrapper = service_wrapper
if labels is None:
Expand Down
7 changes: 7 additions & 0 deletions gwbackupy/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import logging
from json import JSONDecodeError
from typing import IO
import hashlib

import tzlocal
from googleapiclient.errors import HttpError
Expand Down Expand Up @@ -82,3 +83,9 @@ def is_rate_limit_exceeded(e) -> bool:

def random_string(length: int = 8) -> str:
return "".join(random.choice(string.ascii_lowercase) for _ in range(length))


def md5hex(data: bytes | str) -> str:
if isinstance(data, str):
data = data.encode("utf-8")
return hashlib.md5(data).hexdigest().lower()
Loading