def get_smtp_no_tls(config):
    no_tls = False
    config_path = ["app", "smtp", "no_tls"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: no_tls is not found in {config_path_to_string(config_path)}"
        )
    else:
        no_tls = get_config_value(config=config, config_path=config_path)
    return no_tls
def get_smtp_port(config):
    port = None
    config_path = ["app", "smtp", "port"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: port is not found in {config_path_to_string(config_path)}"
        )
    else:
        port = get_config_value(config=config, config_path=config_path)
    return port
def get_photos_sync_interval(config):
    sync_interval = DEFAULT_SYNC_INTERVAL_SEC
    config_path = ["photos", "sync_interval"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"sync_interval is not found in {config_path_to_string(config_path=config_path)}."
            + f" Using default sync_interval: {sync_interval} seconds ...")
    else:
        sync_interval = get_config_value(config=config,
                                         config_path=config_path)
        LOGGER.info(f"Syncing photos every {sync_interval} seconds.")
    return sync_interval
def get_retry_login_interval(config):
    retry_login_interval = DEFAULT_RETRY_LOGIN_INTERVAL_SEC
    config_path = ["app", "credentials", "retry_login_interval"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"retry_login_interval not found in {config_path_to_string(config_path=config_path)}."
            + f" Using default {retry_login_interval} seconds ...")
    else:
        retry_login_interval = get_config_value(config=config,
                                                config_path=config_path)
        LOGGER.info(f"Retrying login every {retry_login_interval} seconds.")
    return retry_login_interval
def prepare_root_destination(config):
    LOGGER.debug("Checking root destination ...")
    root_destination = DEFAULT_ROOT_DESTINATION
    config_path = ["app", "root"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: root destination is missing in {config_path_to_string(config_path)}."
            + f" Using default root destination: {root_destination}", )
    else:
        root_destination = get_config_value(config=config,
                                            config_path=config_path)
    root_destination_path = os.path.abspath(root_destination)
    os.makedirs(root_destination_path, exist_ok=True)
    return root_destination_path
def get_photos_remove_obsolete(config):
    photos_remove_obsolete = False
    config_path = ["photos", "remove_obsolete"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: remove_obsolete is not found in {config_path_to_string(config_path)}."
            + " Not removing the obsolete photos.")
    else:
        photos_remove_obsolete = get_config_value(config=config,
                                                  config_path=config_path)
        LOGGER.debug(
            f"{'R' if photos_remove_obsolete else 'Not R'}emoving obsolete photos ..."
        )
    return photos_remove_obsolete
def get_drive_remove_obsolete(config):
    drive_remove_obsolete = False
    config_path = ["drive", "remove_obsolete"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: remove_obsolete is not found in {config_path_to_string(config_path)}."
            + " Not removing the obsolete files and folders.")
    else:
        drive_remove_obsolete = get_config_value(config=config,
                                                 config_path=config_path)
        LOGGER.debug(
            f"{'R' if drive_remove_obsolete else 'Not R'}emoving obsolete files and folders ..."
        )
    return drive_remove_obsolete
def prepare_drive_destination(config):
    LOGGER.debug("Checking drive destination ...")
    config_path = ["drive", "destination"]
    drive_destination = DEFAULT_DRIVE_DESTINATION
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: destination is missing in {config_path_to_string(config_path)}."
            + f" Using default drive destination: {drive_destination}.")
    else:
        drive_destination = get_config_value(config=config,
                                             config_path=config_path)
    drive_destination_path = os.path.abspath(
        os.path.join(prepare_root_destination(config=config),
                     drive_destination))
    os.makedirs(drive_destination_path, exist_ok=True)
    return drive_destination_path
def prepare_photos_destination(config):
    LOGGER.debug("Checking photos destination ...")
    config_path = ["photos", "destination"]
    photos_destination = DEFAULT_PHOTOS_DESTINATION
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"Warning: destination is missing in {photos_destination}." +
            f" Using default photos destination: {config_path_to_string(config_path)}"
        )
    else:
        photos_destination = get_config_value(config=config,
                                              config_path=config_path)
    photos_destination_path = os.path.abspath(
        os.path.join(prepare_root_destination(config=config),
                     photos_destination))
    os.makedirs(photos_destination_path, exist_ok=True)
    return photos_destination_path
Beispiel #10
0
def send(config, last_send=None, dry_run=False):
    sent_on = None
    email = config_parser.get_smtp_email(config=config)
    to_email = config_parser.get_smtp_to_email(config=config)
    host = config_parser.get_smtp_host(config=config)
    port = config_parser.get_smtp_port(config=config)
    no_tls = config_parser.get_smtp_no_tls(config=config)
    password = config_parser.get_smtp_password(config=config)

    if last_send and last_send > datetime.datetime.now() - datetime.timedelta(hours=24):
        LOGGER.info("Throttling email to once a day")
        sent_on = last_send
    elif email and host and port:
        try:
            sent_on = datetime.datetime.now()
            if not dry_run:
                smtp = smtplib.SMTP(host, port)
                smtp.set_debuglevel(0)
                smtp.connect(host, port)
                if not no_tls:
                    smtp.starttls()

                if password:
                    smtp.login(email, password)

                msg = build_message(email)

                smtp.sendmail(from_addr=email, to_addrs=to_email, msg=msg.as_string())
                smtp.quit()
        except (Exception) as e:
            sent_on = None
            LOGGER.error(f"Failed to send email: {str(e)}.")
    else:
        LOGGER.warning("Not sending 2FA notification because SMTP is not configured")

    return sent_on
def get_photos_filters(config):
    photos_filters = {"albums": None, "file_sizes": ["original"]}
    valid_file_sizes = ["original", "medium", "thumb"]
    config_path = ["photos", "filters"]
    if not traverse_config_path(config=config, config_path=config_path):
        LOGGER.warning(
            f"{config_path_to_string(config_path=config_path)} not found. Downloading all albums with original size ..."
        )
    else:
        config_path.append("albums")
        if (not traverse_config_path(config=config, config_path=config_path)
                or not get_config_value(config=config, config_path=config_path)
                or len(get_config_value(config=config,
                                        config_path=config_path)) == 0):
            LOGGER.warning(
                f"{config_path_to_string(config_path=config_path)} not found. Downloading all albums ..."
            )
        else:
            photos_filters["albums"] = get_config_value(
                config=config, config_path=config_path)
        config_path[2] = "file_sizes"
        if not traverse_config_path(config=config, config_path=config_path):
            LOGGER.warning(
                f"{config_path_to_string(config_path=config_path)} not found. Downloading original size photos ..."
            )
        else:
            file_sizes = get_config_value(config=config,
                                          config_path=config_path)
            for file_size in file_sizes:
                if not file_size in valid_file_sizes:
                    LOGGER.warning(
                        f"Skipping the invalid file size {file_size}, " +
                        f"valid file sizes are {','.join(valid_file_sizes)}.")
                    file_sizes.remove(file_size)
                    if len(file_sizes) == 0:
                        file_sizes = ["original"]
            photos_filters["file_sizes"] = file_sizes
    return photos_filters
Beispiel #12
0
def sync():
    last_send = None
    enable_sync_drive = True
    enable_sync_photos = True
    drive_sync_interval = 0
    photos_sync_interval = 0
    sleep_for = 10
    while True:
        config = read_config()
        username = config_parser.get_username(config=config)
        if username:
            try:
                if ENV_ICLOUD_PASSWORD_KEY in os.environ:
                    password = os.environ.get(ENV_ICLOUD_PASSWORD_KEY)
                    utils.store_password_in_keyring(username=username,
                                                    password=password)
                else:
                    password = utils.get_password_from_keyring(
                        username=username)
                api = ICloudPyService(
                    apple_id=username,
                    password=password,
                    cookie_directory=DEFAULT_COOKIE_DIRECTORY,
                )
                if not api.requires_2sa:
                    if "drive" in config and enable_sync_drive:
                        sync_drive.sync_drive(config=config, drive=api.drive)
                        drive_sync_interval = config_parser.get_drive_sync_interval(
                            config=config)
                    if "photos" in config and enable_sync_photos:
                        sync_photos.sync_photos(config=config,
                                                photos=api.photos)
                        photos_sync_interval = config_parser.get_photos_sync_interval(
                            config=config)
                    if "drive" not in config and "photos" not in config:
                        LOGGER.warning(
                            "Nothing to sync. Please add drive: and/or photos: section in config.yaml file."
                        )
                else:
                    LOGGER.error("Error: 2FA is required. Please log in.")
                    # Retry again
                    sleep_for = config_parser.get_retry_login_interval(
                        config=config)
                    next_sync = (
                        datetime.datetime.now() +
                        datetime.timedelta(seconds=sleep_for)).strftime("%c")
                    LOGGER.info(f"Retrying login at {next_sync} ...")
                    last_send = notify.send(config, last_send)
                    sleep(sleep_for)
                    continue
            except exceptions.ICloudPyNoStoredPasswordAvailableException:
                LOGGER.error(
                    "Password is not stored in keyring. Please save the password in keyring."
                )
                sleep_for = config_parser.get_retry_login_interval(
                    config=config)
                next_sync = (
                    datetime.datetime.now() +
                    datetime.timedelta(seconds=sleep_for)).strftime("%c")
                LOGGER.info(f"Retrying login at {next_sync} ...")
                last_send = notify.send(config, last_send)
                sleep(sleep_for)
                continue

        if "drive" not in config and "photos" in config:
            sleep_for = photos_sync_interval
            enable_sync_drive = False
            enable_sync_photos = True
        elif "drive" in config and "photos" not in config:
            sleep_for = drive_sync_interval
            enable_sync_drive = True
            enable_sync_photos = False
        elif ("drive" in config and "photos" in config
              and drive_sync_interval <= photos_sync_interval):
            sleep_for = photos_sync_interval - drive_sync_interval
            photos_sync_interval -= drive_sync_interval
            enable_sync_drive = True
            enable_sync_photos = False
        else:
            sleep_for = drive_sync_interval - photos_sync_interval
            drive_sync_interval -= photos_sync_interval
            enable_sync_drive = False
            enable_sync_photos = True
        next_sync = (datetime.datetime.now() +
                     datetime.timedelta(seconds=sleep_for)).strftime("%c")
        LOGGER.info(f"Resyncing at {next_sync} ...")
        if (config_parser.get_drive_sync_interval(config=config) < 0 if "drive"
                in config else True and config_parser.get_photos_sync_interval(
                    config=config) < 0 if "photos" in config else True):
            break
        sleep(sleep_for)