diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 79db1047..7078c4dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -547,8 +547,8 @@ jobs: - name: Lint with ruff run: | "$PYTHON" -m pip install ruff - ruff check ../src/gam/ - ruff check ../tests/ + "$PYTHON" -m ruff check ../src/gam/ + "$PYTHON" -m ruff check ../tests/ - name: Install PyInstaller if: matrix.goal == 'build' diff --git a/src/gam/__init__.py b/src/gam/__init__.py index f88164a7..31474788 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -29,39 +29,149 @@ __version__ = '7.46.03' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' # pylint: disable=wrong-import-position +import base64 +import codecs +import collections +import configparser +import csv +from email.charset import add_charset, QP +from email.generator import Generator +from email.header import decode_header, Header +from email import message_from_string +from email.mime.application import MIMEApplication +from email.mime.audio import MIMEAudio +from email.mime.base import MIMEBase +from email.mime.image import MIMEImage +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from email.utils import formatdate +from email.policy import SMTP as policySMTP +import hashlib +from html.entities import name2codepoint +from html.parser import HTMLParser import http.client import importlib +from importlib.metadata import version as lib_version +import io +import ipaddress +import json import logging +from logging.handlers import RotatingFileHandler +import mimetypes +import multiprocessing import os import platform +import queue +import random import re +from secrets import SystemRandom +import shlex +import signal +import smtplib +import socket +import sqlite3 +import ssl +import string +import struct +import subprocess import sys +from tempfile import TemporaryFile +try: + import termios +except ImportError: + # termios does not exist for Windows + pass +import threading +import time from traceback import print_exc import types +from urllib.parse import quote, quote_plus, unquote, urlencode, urlparse, parse_qs +import uuid +import warnings +import webbrowser +import wsgiref.simple_server +import wsgiref.util +import zipfile # disable legacy stuff we don't use and isn't secure os.environ['CRYPTOGRAPHY_OPENSSL_NO_LEGACY'] = "1" +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID # Add package directory to sys.path for source installs (not needed for frozen/PyInstaller builds) if not getattr(sys, 'frozen', False): sys.path.insert(0, os.path.dirname(os.path.realpath(__file__))) +import arrow + +from pathvalidate import sanitize_filename, sanitize_filepath + +import google.oauth2.credentials +import google.oauth2.id_token +import google.auth +import google.auth.transport.requests +from google.auth.compute_engine import _metadata as gce_metadata +from google.auth.jwt import Credentials as JWTCredentials +import google.oauth2.service_account +import google_auth_oauthlib.flow +import google_auth_httplib2 +import googleapiclient +import googleapiclient.discovery +import googleapiclient.errors +import googleapiclient.http import httplib2 + httplib2.RETRIES = 5 +from passlib.hash import sha512_crypt +from filelock import FileLock + +if platform.system() == 'Linux': + import distro + +from gamlib import action from gamlib import api as API from gamlib import settings as GC +from gamlib import clargs +from gamlib import entity from gamlib import gapi as GAPI +from gamlib import gdata as GDATA from gamlib import state as GM +from gamlib import indent from gamlib import msgs as Msg +from gamlib import skus as SKU +from gamlib import uprop as UProp +from gamlib import verlibs -from gam.util.api import SvcAcctAPIDisabledExit # noqa: E402 -from gam.util.email import send_email # noqa: E402 +import gdata.apps.service +import gdata.apps.audit +import gdata.apps.audit.service +import gdata.apps.contacts +import gdata.apps.contacts.service + +from gam.util.html import _DeHTMLParser, dehtml # noqa: F401 # re-export +from gam.util.access import ( # noqa: F401 # re-export + accessErrorMessage, accessErrorExit, accessErrorExitNonDirectory, + checkEntityDNEorAccessErrorExit, checkEntityAFDNEorAccessErrorExit, + checkEntityItemValueAFDNEorAccessErrorExit, + entityUnknownWarning, entityOrEntityUnknownWarning, duplicateAliasGroupUserWarning, +) +from gam.util.api import ( # noqa: F401 # re-export (moved from access.py) + ClientAPIAccessDeniedExit, SvcAcctAPIAccessDenied, SvcAcctAPIAccessDeniedExit, + SvcAcctAPIDisabledExit, APIAccessDeniedExit, +) +from gam.util.email import ( # noqa: F401 # re-export + _addAttachmentsToMessage, _addEmbeddedImagesToMessage, send_email, +) from gam.constants import * -from util.fileio import adjustRedirectedSTDFilesIfNotMultiprocessing, closeSTDFilesIfNotMultiprocessing # noqa: E402 +from util.output import ISOformatTimeStamp, currentISOformatTimeStamp # noqa: E402,F401 +from util.fileio import adjustRedirectedSTDFilesIfNotMultiprocessing, closeSTDFilesIfNotMultiprocessing # noqa: E402,F401 from gam.var import Act, Cmd, Ent, Ind # noqa: E402,F401 @@ -80,18 +190,20 @@ else: GM.Globals[GM.GAM_TYPE] = 'pythonsource' -from util.output import showAPICallsRetryData # noqa: E402 +from util.output import redactable_debug_print, showAPICallsRetryData # noqa: E402,F401 # Multiprocessing lock mplock = None # Imports used by __init__.py's own dispatch tables and functions -from util.output import setSysExitRC, printErrorMessage # noqa: E402 -from util.args import getChoice # noqa: E402 -from util.args import checkArgumentPresent, NO_DEFAULT # noqa: E402 +from util.output import setSysExitRC, systemErrorExit, printErrorMessage # noqa: E402 +from util.output import writeStderr, formatKeyValueList # noqa: E402 +from util.errors import unknownArgumentExit # noqa: E402 +from util.args import getArgument, getChoice, getInteger, getStringReturnInList # noqa: E402 +from util.args import checkArgumentPresent, normalizeEmailAddressOrUID, NO_DEFAULT # noqa: E402 from util.connection import doCheckConnection, doComment, doVersion, doUsage # noqa: E402 -from util.entity import getEntityArgument, getEntityToModify # noqa: E402 +from util.entity import getEntityArgument, getEntityList, getEntityToModify # noqa: E402 from util.config import SetGlobalVariables # noqa: E402 from util.fileio import closeGAMCommandLog, writeGAMCommandLog # noqa: E402 from util.batch import ( # noqa: E402 diff --git a/src/gam/cmd/admin.py b/src/gam/cmd/admin.py index 864d3246..54a9b538 100644 --- a/src/gam/cmd/admin.py +++ b/src/gam/cmd/admin.py @@ -3,6 +3,7 @@ import json +import re from gamlib import api as API from gamlib import settings as GC diff --git a/src/gam/cmd/alerts.py b/src/gam/cmd/alerts.py index 59a2fd72..d3261bc7 100644 --- a/src/gam/cmd/alerts.py +++ b/src/gam/cmd/alerts.py @@ -1,11 +1,15 @@ """GAM alert center management.""" import json +import sys from gam.util.args import formatLocalTime from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import _getAdminEmail from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/aliases.py b/src/gam/cmd/aliases.py index ea7e739d..10b36014 100644 --- a/src/gam/cmd/aliases.py +++ b/src/gam/cmd/aliases.py @@ -6,6 +6,7 @@ import time from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import entityUnknownWarning @@ -44,6 +45,7 @@ from gam.util.entity import ( getEntityArgument, getEntityList, getEntityToModify, + getQueries, ) from gam.util.errors import entityActionFailedExit, unknownArgumentExit from gam.util.orgunits import getOrgUnitItem diff --git a/src/gam/cmd/analytics.py b/src/gam/cmd/analytics.py index 111c3346..2913460e 100644 --- a/src/gam/cmd/analytics.py +++ b/src/gam/cmd/analytics.py @@ -5,6 +5,8 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPIpages diff --git a/src/gam/cmd/audit.py b/src/gam/cmd/audit.py index 2886fded..c12b720d 100644 --- a/src/gam/cmd/audit.py +++ b/src/gam/cmd/audit.py @@ -3,8 +3,13 @@ Mailbox monitor creation/deletion/listing and the doWhatIs command. """ +import re +from gamlib import api as API +from gamlib import settings as GC +from gamlib import gapi as GAPI from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import entityUnknownWarning from gam.util.api import getEmailAuditObject diff --git a/src/gam/cmd/browsers.py b/src/gam/cmd/browsers.py index 72479e19..22b87b4a 100644 --- a/src/gam/cmd/browsers.py +++ b/src/gam/cmd/browsers.py @@ -5,6 +5,7 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import checkEntityAFDNEorAccessErrorExit diff --git a/src/gam/cmd/caa.py b/src/gam/cmd/caa.py index c9f09219..40dabcc8 100644 --- a/src/gam/cmd/caa.py +++ b/src/gam/cmd/caa.py @@ -4,7 +4,9 @@ import json import string from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/chat/setup.py b/src/gam/cmd/chat/setup.py index af782c49..df47735c 100644 --- a/src/gam/cmd/chat/setup.py +++ b/src/gam/cmd/chat/setup.py @@ -6,6 +6,7 @@ Part of the _chat_tmp sub-package.""" import re import json +import sys import base64 import os diff --git a/src/gam/cmd/chat/spaces.py b/src/gam/cmd/chat/spaces.py index 70152c8a..da6a9407 100644 --- a/src/gam/cmd/chat/spaces.py +++ b/src/gam/cmd/chat/spaces.py @@ -5,11 +5,13 @@ Part of the _chat_tmp sub-package.""" """GAM Google Chat management.""" import json +import sys import uuid from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api import buildGAPIObject from gam.util.api_call import callGAPI, callGAPIpages diff --git a/src/gam/cmd/chromeapps.py b/src/gam/cmd/chromeapps.py index 5add58c5..e0bf1a91 100644 --- a/src/gam/cmd/chromeapps.py +++ b/src/gam/cmd/chromeapps.py @@ -6,6 +6,7 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import checkEntityAFDNEorAccessErrorExit diff --git a/src/gam/cmd/chromepolicies.py b/src/gam/cmd/chromepolicies.py index 20a67ed4..6c2943dd 100644 --- a/src/gam/cmd/chromepolicies.py +++ b/src/gam/cmd/chromepolicies.py @@ -8,6 +8,7 @@ import os from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import entityUnknownWarning @@ -70,6 +71,7 @@ from gam.util.errors import ( usageErrorExit, ) from gam.util.fileio import UNKNOWN, setFilePath +from gam.util.orgunits import getOrgUnitId from gam.constants import DFA_URL diff --git a/src/gam/cmd/cigroups/members.py b/src/gam/cmd/cigroups/members.py index 6dde6cb5..999e409c 100644 --- a/src/gam/cmd/cigroups/members.py +++ b/src/gam/cmd/cigroups/members.py @@ -12,6 +12,7 @@ from gam.util.args import formatLocalTime from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind @@ -123,7 +124,7 @@ def getCIGroupMemberTypes(myarg, typesSet): def doInfoCIGroups(): from gam.cmd.groups.members import CIGROUP_FIELDS_CHOICE_MAP, CIGROUP_TIME_OBJECTS, _checkCIMemberMatch, _showCIGroup, finalizeIPSGMGroupRolesMemberDisplayOptions, getIPSGMGroupRolesMemberDisplayOptions, getMemberMatchOptions, initIPSGMGroupMemberDisplayOptions, initMemberOptions def printCIGroupMemberTree(group_id, showRole): - if group_id not in cachedGroupMembers: + if not group_id in cachedGroupMembers: try: cachedGroupMembers[group_id] = callGAPIpages(ci.groups().memberships(), 'list', 'memberships', throwReasons=GAPI.MEMBERS_THROW_REASONS, diff --git a/src/gam/cmd/ciuserinvitations.py b/src/gam/cmd/ciuserinvitations.py index c59e8632..2141c643 100644 --- a/src/gam/cmd/ciuserinvitations.py +++ b/src/gam/cmd/ciuserinvitations.py @@ -4,7 +4,10 @@ import re import json from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import entityUnknownWarning from gam.util.api import buildGAPIObject diff --git a/src/gam/cmd/cloudstorage.py b/src/gam/cmd/cloudstorage.py index 10e16c2e..9a546e32 100644 --- a/src/gam/cmd/cloudstorage.py +++ b/src/gam/cmd/cloudstorage.py @@ -4,6 +4,7 @@ import re import googleapiclient.http import hashlib +import copy import base64 import os import time @@ -11,6 +12,7 @@ import time from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject diff --git a/src/gam/cmd/contacts.py b/src/gam/cmd/contacts.py index 885110bb..0c246919 100644 --- a/src/gam/cmd/contacts.py +++ b/src/gam/cmd/contacts.py @@ -5,7 +5,10 @@ import json import gdata.apps.contacts +from gamlib import api as API from gamlib import settings as GC +from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.constants import NO_ENTITIES_FOUND_RC @@ -51,6 +54,7 @@ from gam.util.display import ( from gam.util.entity import getEntityList from gam.util.errors import invalidChoiceExit, missingArgumentExit, unknownArgumentExit, usageErrorExit from gam.util.output import setSysExitRC, writeStdout +from gam.cmd.users.manage import ADDRESS_ARGUMENT_TO_FIELD_MAP, ORGANIZATION_ARGUMENT_TO_FIELD_MAP def _getCreateContactReturnOptions(parameters): @@ -107,7 +111,7 @@ CONTACT_RELATIONS = 'Relations' CONTACT_USER_DEFINED_FIELDS = 'User Defined Fields' CONTACT_WEBSITES = 'Websites' # -class ContactsManager: +class ContactsManager(): CONTACT_ARGUMENT_TO_PROPERTY_MAP = { 'json': CONTACT_JSON, @@ -1663,7 +1667,7 @@ PEOPLE_REMOVE_GROUPS_LIST = 'ContactRemoveGroupsList' PEOPLE_GROUP_NAME = 'name' PEOPLE_GROUP_CLIENT_DATA = 'clientData' # -class PeopleManager: +class PeopleManager(): PEOPLE_ARGUMENT_TO_PROPERTY_MAP = { 'json': PEOPLE_JSON, 'additionalname': PEOPLE_NAMES_MIDDLE_NAME, diff --git a/src/gam/cmd/courses/content.py b/src/gam/cmd/courses/content.py index 6df04c5a..b5a221a7 100644 --- a/src/gam/cmd/courses/content.py +++ b/src/gam/cmd/courses/content.py @@ -4,12 +4,15 @@ Part of the _courses_tmp sub-package.""" """GAM Google Classroom course management.""" +import re import json +import sys from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.api import buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages @@ -32,7 +35,7 @@ from gam.util.csv_pf import ( from gam.util.display import entityActionFailedWarning, entityDoesNotHaveItemWarning, getPageMessageForWhom, printGettingAllEntityItemsForWhom from gam.util.entity import getEntityList -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind def doPrintCourseAnnouncements(): def _printCourseAnnouncement(course, courseAnnouncement, i, count): diff --git a/src/gam/cmd/courses/courses.py b/src/gam/cmd/courses/courses.py index 1f09f59e..cca093a9 100644 --- a/src/gam/cmd/courses/courses.py +++ b/src/gam/cmd/courses/courses.py @@ -143,7 +143,7 @@ def _gettingCourseEntityQuery(entityType, courseWorkStates): query = query[:-2] return query -class CourseAttributes: +class CourseAttributes(): def __init__(self, croom, updateMode): self.croom = croom diff --git a/src/gam/cmd/courses/guardians.py b/src/gam/cmd/courses/guardians.py index e3bac32b..a6f867b4 100644 --- a/src/gam/cmd/courses/guardians.py +++ b/src/gam/cmd/courses/guardians.py @@ -4,6 +4,7 @@ Part of the _courses_tmp sub-package.""" """GAM Google Classroom course management.""" +import re import json from gamlib import uprop as UProp @@ -11,6 +12,7 @@ from gamlib import uprop as UProp from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.access import entityUnknownWarning from gam.util.api import buildGAPIObject diff --git a/src/gam/cmd/cros.py b/src/gam/cmd/cros.py index e502cacb..1bd40d5c 100644 --- a/src/gam/cmd/cros.py +++ b/src/gam/cmd/cros.py @@ -1,6 +1,7 @@ """GAM ChromeOS device management.""" import json +import sys from gam.util.args import formatLocalTime @@ -39,6 +40,7 @@ from gam.util.args import ( getString, getStringWithCRsNLs, getTimeOrDeltaFromNow, + getYYYYMMDD, makeOrgUnitPathAbsolute, makeOrgUnitPathRelative, substituteQueryTimes, diff --git a/src/gam/cmd/customer.py b/src/gam/cmd/customer.py index 0ec0a8e6..f46ca687 100644 --- a/src/gam/cmd/customer.py +++ b/src/gam/cmd/customer.py @@ -5,11 +5,12 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import accessErrorExit from gam.util.api import buildGAPIObject -from gam.util.api_call import callGAPI +from gam.util.api_call import callGAPI, callGAPIitems from gam.util.args import ( LANGUAGE_CODES_MAP, YYYYMMDD_FORMAT, @@ -34,8 +35,13 @@ from gam.util.errors import unknownArgumentExit from gam.util.fileio import UNKNOWN from gam.util.output import printWarningMessage, writeStdout from gam.util.entity import ( + PRINT_PRIVILEGES_FIELDS, _getCustomerId, + _getCustomerIdNoC, + _getCustomersCustomerIdNoC, + _getCustomersCustomerIdWithC, _getDomainList, + setTrueCustomerId, ) from gam.constants import DATA_NOT_AVALIABLE_RC from gam.cmd.domains import CUSTOMER_LICENSE_MAP diff --git a/src/gam/cmd/datatransfer.py b/src/gam/cmd/datatransfer.py index d27115ec..25c44736 100644 --- a/src/gam/cmd/datatransfer.py +++ b/src/gam/cmd/datatransfer.py @@ -1,5 +1,6 @@ """GAM data transfer operations.""" +import sys from gam.util.args import formatLocalTime import time @@ -7,6 +8,7 @@ import time from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject diff --git a/src/gam/cmd/delegates.py b/src/gam/cmd/delegates.py index e8d296ae..0b562b29 100644 --- a/src/gam/cmd/delegates.py +++ b/src/gam/cmd/delegates.py @@ -4,6 +4,8 @@ from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject from gam.util.api_call import callGAPI, callGAPIpages diff --git a/src/gam/cmd/domains.py b/src/gam/cmd/domains.py index ffacf56e..ccb82e3e 100644 --- a/src/gam/cmd/domains.py +++ b/src/gam/cmd/domains.py @@ -1,11 +1,14 @@ """GAM domain and domain alias management.""" import json +import sys +import re from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import accessErrorExit diff --git a/src/gam/cmd/drive/activity.py b/src/gam/cmd/drive/activity.py index 3a15f261..6ac5295d 100644 --- a/src/gam/cmd/drive/activity.py +++ b/src/gam/cmd/drive/activity.py @@ -4,13 +4,17 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import json +import sys from gam.util.args import formatLocalTime from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.api import _getAdminEmail, buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems, callGAPIpages, yieldGAPIpages diff --git a/src/gam/cmd/drive/copymove/copymove_move.py b/src/gam/cmd/drive/copymove/copymove_move.py index 6c19fac7..fe5037f6 100644 --- a/src/gam/cmd/drive/copymove/copymove_move.py +++ b/src/gam/cmd/drive/copymove/copymove_move.py @@ -8,13 +8,16 @@ Part of the copymove sub-package.""" """GAM Google Drive file, permission, shared drive, and label management.""" +import re from gam.cmd.drive.core import DFA_SEARCHARGS from gam.cmd.drive.copymove.copymove_util import _checkForDuplicateTargetFile, _checkForExistingShortcut, _copyPermissions, _getCopyFolderNonInheritedPermissions, _getCopyMoveParentInfo, _getCopyMoveTargetInfo, _getUniqueFilename, _identicalSourceTarget, _printStatistics, _targetFilenameExists, _verifyUserIsOrganizer, getCopyMoveOptions, initCopyMoveOptions +from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api_call import callGAPI, callGAPIpages from gam.util.args import getArgument, getBoolean diff --git a/src/gam/cmd/drive/copymove/copymove_util.py b/src/gam/cmd/drive/copymove/copymove_util.py index 241e681e..a8eeaf2f 100644 --- a/src/gam/cmd/drive/copymove/copymove_util.py +++ b/src/gam/cmd/drive/copymove/copymove_util.py @@ -16,6 +16,7 @@ import os from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages @@ -1490,9 +1491,9 @@ def copyDriveFile(users): elif (sourceMimeType == MIMETYPE_GA_FOLDER) and copyMoveOptions['mergeWithParent']: destName = dest['name'] elif ((newParentsSpecified and newParentId not in sourceParents) or - (newParentId in sourceParents and + ((newParentId in sourceParents and (sourceMimeType == MIMETYPE_GA_FOLDER and copyMoveOptions['duplicateFolders'] != DUPLICATE_FOLDER_MERGE) or - (sourceMimeType != MIMETYPE_GA_FOLDER and copyMoveOptions['duplicateFiles'] not in [DUPLICATE_FILE_OVERWRITE_ALL, DUPLICATE_FILE_OVERWRITE_OLDER]))): + (sourceMimeType != MIMETYPE_GA_FOLDER and copyMoveOptions['duplicateFiles'] not in [DUPLICATE_FILE_OVERWRITE_ALL, DUPLICATE_FILE_OVERWRITE_OLDER])))): if copyMoveOptions['replaceFilename']: destName = processFilenameReplacements(sourceName, copyMoveOptions['replaceFilename']) else: diff --git a/src/gam/cmd/drive/core.py b/src/gam/cmd/drive/core.py index 9648b3df..1d768ae6 100644 --- a/src/gam/cmd/drive/core.py +++ b/src/gam/cmd/drive/core.py @@ -64,7 +64,7 @@ from gam.util.fileio import FILE_ERROR_RC, fileErrorMessage, setFilePath from gam.util.output import setSysExitRC, stderrWarningMsg, systemErrorExit from gam.constants import ANY_NON_TRASHED_FOLDER_NAME, MY_NON_TRASHED_FOLDER_NAME, NO_ENTITIES_FOUND_RC, TEAM_DRIVE -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind APPLICATION_VND_GOOGLE_APPS = 'application/vnd.google-apps.' MIMETYPE_GA_DOCUMENT = f'{APPLICATION_VND_GOOGLE_APPS}document' @@ -649,7 +649,7 @@ def _getDriveFileParentInfo(drive, user, i, count, body, parameters, emptyQueryO fileId=parameters[DFA_PARENTID], fields='id,name,mimeType,driveId', supportsAllDrives=True) if result['mimeType'] != MIMETYPE_GA_FOLDER: entityActionNotPerformedWarning([Ent.USER, user, entityType, None], - f'parentid: {parameters[DFA_PARENTID]}, {Msg.NOT_AN_ENTITY.format(Ent.Singular(Ent.DRIVE_FOLDER))}', i, count) + f'parentid: {parameters[DFA_PARENTID]}, {Msg.NOT_AN_ENTITY.format((Ent.Singular(Ent.DRIVE_FOLDER)))}', i, count) return False body['parents'].append(result['id']) if result.get('driveId'): @@ -833,7 +833,7 @@ def validateMimeType(mimeType): def getMimeType(): return validateMimeType(getString(Cmd.OB_MIMETYPE).lower()) -class MimeTypeCheck: +class MimeTypeCheck(): def __init__(self): self.mimeTypes = set() diff --git a/src/gam/cmd/drive/fileinfo.py b/src/gam/cmd/drive/fileinfo.py index fbb2a2f3..9752ad42 100644 --- a/src/gam/cmd/drive/fileinfo.py +++ b/src/gam/cmd/drive/fileinfo.py @@ -4,14 +4,16 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import platform -from gam.cmd.drive.core import _validateUserGetFileIDs, getDriveFileEntity +from gam.cmd.drive.core import _getDriveFileNameFromId, _validateUserGetFileIDs, getDriveFileEntity import os from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.constants import WITH_PARENTS @@ -46,7 +48,7 @@ SHARED_WITHME = 'SharedWithMe' SHARED_DRIVES = 'SharedDrives' from gam.cmd.drive.core import ( - _getSharedDriveNameFromId, _simpleFileIdEntityList, + MimeTypeCheck, _getSharedDriveNameFromId, _simpleFileIdEntityList, _validateUserGetFileIDs, _validateUserSharedDrive, cleanFileIDsList, getDriveFileEntity, getSharedDriveEntity, initDriveFileEntity, ) diff --git a/src/gam/cmd/drive/filelist.py b/src/gam/cmd/drive/filelist.py index 42c30977..966b84a1 100644 --- a/src/gam/cmd/drive/filelist.py +++ b/src/gam/cmd/drive/filelist.py @@ -4,6 +4,7 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import json from gam.util.args import formatLocalTime @@ -13,6 +14,7 @@ from gam.cmd.drive.filepaths import _finalizeIncludeLabels, _finalizeIncludePerm from gam.util.csv_pf import DEFAULT_SKIP_OBJECTS +from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI from gamlib import state as GM diff --git a/src/gam/cmd/drive/filepaths.py b/src/gam/cmd/drive/filepaths.py index 89525f12..05831b75 100644 --- a/src/gam/cmd/drive/filepaths.py +++ b/src/gam/cmd/drive/filepaths.py @@ -4,6 +4,7 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import json from gam.cmd.drive.core import _getSharedDriveNameFromId, _validateUserGetFileIDs, getDriveFileEntity @@ -12,11 +13,14 @@ from gam.cmd.drive.labels import normalizeDriveLabelID from gam.util.csv_pf import DEFAULT_SKIP_OBJECTS +from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.constants import MY_DRIVE, TEAM_DRIVE -from gam.var import Cmd, Ent, Ind +from gam.var import Act, Cmd, Ent, Ind APPLICATION_VND_GOOGLE_APPS = 'application/vnd.google-apps.' MIMETYPE_GA_DOCUMENT = f'{APPLICATION_VND_GOOGLE_APPS}document' @@ -50,6 +54,8 @@ from gam.cmd.drive.core import DRIVE_LABEL_CHOICE_MAP # cross-module ref from gam.util.api_call import callGAPI, callGAPIitems, callGAPIpages from gam.util.args import ( OrderBy, + StartEndTime, + checkArgumentPresent, escapeCRsNLs, getArgument, getBoolean, @@ -569,7 +575,7 @@ def _getDriveFieldSubField(field, fieldsList, parentsSubFields): else: invalidChoiceExit(field, list(DRIVE_SUBFIELDS_CHOICE_MAP), True) -class DriveFileFields: +class DriveFileFields(): def __init__(self): self.showSharedDriveNames = False self.allFields = False diff --git a/src/gam/cmd/drive/files.py b/src/gam/cmd/drive/files.py index 88e5d530..573491a5 100644 --- a/src/gam/cmd/drive/files.py +++ b/src/gam/cmd/drive/files.py @@ -8,10 +8,12 @@ import re from gam.cmd.drive.core import DFA_IGNORE_DEFAULT_VISIBILITY, DFA_KEEP_REVISION_FOREVER, DFA_LOCALFILENAME, DFA_LOCALFILEPATH, DFA_LOCALMIMETYPE, DFA_OCRLANGUAGE, DFA_PARENTID, DFA_PRESERVE_FILE_TIMES, DFA_REPLACEFILENAME, DFA_SHAREDDRIVE_PARENT, DFA_STRIPNAMEPREFIX, DFA_TIMEFORMAT, DFA_TIMESTAMP, DFA_URL, DFA_USE_CONTENT_AS_INDEXABLE_TEXT, _getDriveFileAddRemoveParentInfo, _getDriveFileParentInfo, _validateUserGetFileIDs, escapeDriveFileName, getDriveFileAddRemoveParentAttribute, getDriveFileAttribute, getDriveFileEntity, getDriveFileParentAttribute, getMediaBody, initDriveFileAttributes, setPreservedFileTimes from gam.cmd.drive.fileinfo import writeReturnIdLink +import time from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems, callGAPIpages diff --git a/src/gam/cmd/drive/filetree.py b/src/gam/cmd/drive/filetree.py index 942849b9..6dd11732 100644 --- a/src/gam/cmd/drive/filetree.py +++ b/src/gam/cmd/drive/filetree.py @@ -9,12 +9,15 @@ import re from gam.cmd.drive.core import _getSharedDriveNameFromId, _mapDrive2QueryToDrive3, cleanFileIDsList, escapeDriveFileName, getEscapedDriveFileName, initDriveFileEntity from gam.cmd.drive.revisions import _stripMeInOwners, _stripNotMeInOwners, _updateAnyOwnerQuery +from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.entity import QUERY_SHORTCUTS_MAP from gam.constants import MY_DRIVE, TEAM_DRIVE -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind APPLICATION_VND_GOOGLE_APPS = 'application/vnd.google-apps.' MIMETYPE_GA_DOCUMENT = f'{APPLICATION_VND_GOOGLE_APPS}document' @@ -62,6 +65,7 @@ from gam.util.args import ( StartEndTime, checkArgumentPresent, checkGetArgument, + escapeCRsNLs, getArgument, getBoolean, getChoice, @@ -254,7 +258,7 @@ DRIVEFILE_ACL_ROLES_MAP = { DRIVEFILE_ACL_PERMISSION_TYPES = ['anyone', 'domain', 'group', 'user'] # anyone must be first element DRIVEFILE_ACL_PERMISSION_DETAILS_TYPES = ['file', 'member'] -class PermissionMatch: +class PermissionMatch(): _PERMISSION_MATCH_ACTION_MAP = {'process': True, 'skip': False} _PERMISSION_MATCH_MODE_MAP = {'or': True, 'and': False} @@ -501,7 +505,7 @@ def noFileSelectFileIdEntity(fileIdEntity): SHOW_OWNED_BY_CHOICE_MAP = {'any': None, 'me': True, 'others': False} -class DriveListParameters: +class DriveListParameters(): def __init__(self, myargOptions): self.PM = PermissionMatch() self.myargOptions = myargOptions diff --git a/src/gam/cmd/drive/labels.py b/src/gam/cmd/drive/labels.py index 07b3240e..7540e177 100644 --- a/src/gam/cmd/drive/labels.py +++ b/src/gam/cmd/drive/labels.py @@ -10,7 +10,9 @@ import json from gam.cmd.drive.core import _validateUserGetFileIDs, getDriveFileEntity from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api import _getAdminEmail from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/drive/looker.py b/src/gam/cmd/drive/looker.py index 7a8ed9c7..290a0ad3 100644 --- a/src/gam/cmd/drive/looker.py +++ b/src/gam/cmd/drive/looker.py @@ -4,11 +4,15 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import json +import sys from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages from gam.util.args import ( diff --git a/src/gam/cmd/drive/permissions.py b/src/gam/cmd/drive/permissions.py index 4325a430..5962edeb 100644 --- a/src/gam/cmd/drive/permissions.py +++ b/src/gam/cmd/drive/permissions.py @@ -4,6 +4,7 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re import json from gam.cmd.drive.core import _getDriveFileNameFromId, _getSharedDriveNameFromId, _validateUserGetFileIDs, _validateUserSharedDrive, cleanFileIDsList, getDriveFileEntity, getSharedDriveEntity, initDriveFileEntity diff --git a/src/gam/cmd/drive/revisions.py b/src/gam/cmd/drive/revisions.py index c44a6973..11df4e80 100644 --- a/src/gam/cmd/drive/revisions.py +++ b/src/gam/cmd/drive/revisions.py @@ -4,9 +4,11 @@ """GAM Google Drive file, permission, shared drive, and label management.""" +import re from gam.cmd.drive.core import _getDriveFileNameFromId, _validateUserGetFileIDs, getDriveFileEntity +from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI from gamlib import state as GM diff --git a/src/gam/cmd/drive/shareddrives.py b/src/gam/cmd/drive/shareddrives.py index a1561971..c20b347c 100644 --- a/src/gam/cmd/drive/shareddrives.py +++ b/src/gam/cmd/drive/shareddrives.py @@ -6,6 +6,7 @@ import re import json +import sys from gam.util.args import formatLocalTime @@ -21,6 +22,7 @@ from gam.cmd.drive.core import getSharedDriveEntity, _validateUserSharedDrive from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api import _getAdminEmail, _getValueFromOAuth, buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/drive/transfer/ownership.py b/src/gam/cmd/drive/transfer/ownership.py index fcddde12..7bb766b1 100644 --- a/src/gam/cmd/drive/transfer/ownership.py +++ b/src/gam/cmd/drive/transfer/ownership.py @@ -8,6 +8,8 @@ Part of the transfer sub-package.""" """GAM Google Drive file, permission, shared drive, and label management.""" +import re +import sys from gamlib import api as API from gamlib import settings as GC diff --git a/src/gam/cmd/gmail/cse.py b/src/gam/cmd/gmail/cse.py index f7ff3c14..d6ff651b 100644 --- a/src/gam/cmd/gmail/cse.py +++ b/src/gam/cmd/gmail/cse.py @@ -4,12 +4,15 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re import json +import sys import os from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages diff --git a/src/gam/cmd/gmail/delegates.py b/src/gam/cmd/gmail/delegates.py index fa332f21..34579ef6 100644 --- a/src/gam/cmd/gmail/delegates.py +++ b/src/gam/cmd/gmail/delegates.py @@ -4,10 +4,12 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api import buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/gmail/filters.py b/src/gam/cmd/gmail/filters.py index ecc17b1f..3fa29a83 100644 --- a/src/gam/cmd/gmail/filters.py +++ b/src/gam/cmd/gmail/filters.py @@ -4,13 +4,16 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re import json +import sys from gam.cmd.gmail.labels import _getLabelId, _getLabelName, _getLabelSet, _getUserGmailLabels, buildLabelPath from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems diff --git a/src/gam/cmd/gmail/forms.py b/src/gam/cmd/gmail/forms.py index d5c662d7..13504e55 100644 --- a/src/gam/cmd/gmail/forms.py +++ b/src/gam/cmd/gmail/forms.py @@ -4,10 +4,14 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re import json from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages from gam.util.args import ( diff --git a/src/gam/cmd/gmail/labels.py b/src/gam/cmd/gmail/labels.py index fc57de33..c607d6a1 100644 --- a/src/gam/cmd/gmail/labels.py +++ b/src/gam/cmd/gmail/labels.py @@ -9,6 +9,7 @@ import re from gam.util.csv_pf import RI_ENTITY, RI_J, RI_JCOUNT, RI_ITEM from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI from gamlib import state as GM from gamlib import msgs as Msg @@ -768,7 +769,7 @@ def _convertLabelNamesToIds(gmail, user, i, count, bodyLabels, labelNameMap, add # make sure to create parent labels for proper nesting parent_label = label[:label.rfind('/')] while True: - if (parent_label not in labelNameMap) and (parent_label.upper() not in labelNameMap): + if (not parent_label in labelNameMap) and (not parent_label.upper() in labelNameMap): result = callGAPI(gmail.users().labels(), 'create', userId='me', body={'name': parent_label}, fields='id') labelNameMap[parent_label] = labelNameMap[parent_label.upper()] = result['id'] diff --git a/src/gam/cmd/gmail/profile.py b/src/gam/cmd/gmail/profile.py index 06ce0896..c07cbb06 100644 --- a/src/gam/cmd/gmail/profile.py +++ b/src/gam/cmd/gmail/profile.py @@ -4,12 +4,17 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re import json +import sys import uuid import base64 from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.api import buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages @@ -25,7 +30,7 @@ from gam.util.display import ( from gam.util.entity import getEntityArgument from gam.util.errors import unknownArgumentExit -from gam.var import Act, Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind def watchGmail(users): maxMessages = 100 diff --git a/src/gam/cmd/gmail/settings.py b/src/gam/cmd/gmail/settings.py index c05cb2b7..16d7cfe6 100644 --- a/src/gam/cmd/gmail/settings.py +++ b/src/gam/cmd/gmail/settings.py @@ -4,12 +4,15 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re from gam.cmd.gmail.messages import forwardMessagesThreads from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems from gam.util.args import ( diff --git a/src/gam/cmd/gmail/signature.py b/src/gam/cmd/gmail/signature.py index c3dca7fa..a8e549c4 100644 --- a/src/gam/cmd/gmail/signature.py +++ b/src/gam/cmd/gmail/signature.py @@ -4,11 +4,15 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re from gam.cmd.gmail.settings import _processSendAs, _processSignature, getSendAsAttributes from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI from gam.util.args import ( diff --git a/src/gam/cmd/gmail/smime.py b/src/gam/cmd/gmail/smime.py index f308a0fd..60535fb8 100644 --- a/src/gam/cmd/gmail/smime.py +++ b/src/gam/cmd/gmail/smime.py @@ -4,6 +4,7 @@ Part of the _gmail_monolith sub-package.""" """GAM Gmail management: labels, messages, filters, forwarding, sendas, S/MIME, CSE, vacation.""" +import re from gam.util.args import formatLocalTimestamp import base64 @@ -11,6 +12,7 @@ import base64 from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems diff --git a/src/gam/cmd/groups/members.py b/src/gam/cmd/groups/members.py index aa1c51fd..c236f352 100644 --- a/src/gam/cmd/groups/members.py +++ b/src/gam/cmd/groups/members.py @@ -78,6 +78,7 @@ from gam.util.display import ( printGettingEntityItem, printGettingEntityItemForWhom, printKeyValueList, + printKeyValueListWithCount, printKeyValueWithCRsNLs, printLine, ) @@ -107,7 +108,7 @@ from gam.constants import ( ) from gam.util.domain_filters import ( getUserGroupDomainQueryFilters, initUserGroupDomainQueryFilters, - makeUserGroupDomainQueryFilters, + makeUserGroupDomainQueryFilters, _setUserGroupArgs, ) from gam.util.schema_utils import _initSchemaParms, _getSchemaNameList from gam.util.output import executeBatch, writeStderr, writeStdout @@ -1442,11 +1443,11 @@ def getGroupMembers(cd, groupEmail, memberRoles, membersList, membersSet, i, cou groupMemberList = [] for member in groupMembers: if member['type'] != Ent.TYPE_GROUP: - if (member['type'] in typesSet and + if ((member['type'] in typesSet and _checkMemberMatch(member, memberOptions) and _checkMemberRoleIsSuspendedIsArchived(member, validRoles, memberOptions[MEMBEROPTION_ISSUSPENDED], memberOptions[MEMBEROPTION_ISARCHIVED]) and (not checkShowCategory or _checkMemberCategory(member, memberDisplayOptions)) and - member['id'] not in membersSet): + member['id'] not in membersSet)): if memberOptions[MEMBEROPTION_GETDELIVERYSETTINGS]: _getMemberDeliverySettings(member) membersSet.add(member['id']) diff --git a/src/gam/cmd/licenses.py b/src/gam/cmd/licenses.py index c70c0edd..9e65c2a8 100644 --- a/src/gam/cmd/licenses.py +++ b/src/gam/cmd/licenses.py @@ -5,10 +5,11 @@ from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI from gamlib import state as GM +from gamlib import msgs as Msg from gamlib import skus as SKU -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject from gam.util.api_call import callGAPIpages from gam.util.args import getArgument, getGoogleProductList, getGoogleSKUList, getInteger diff --git a/src/gam/cmd/meet.py b/src/gam/cmd/meet.py index ad098327..4111f416 100644 --- a/src/gam/cmd/meet.py +++ b/src/gam/cmd/meet.py @@ -3,7 +3,10 @@ import json from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages diff --git a/src/gam/cmd/mobile.py b/src/gam/cmd/mobile.py index 0a6409f3..76ed449e 100644 --- a/src/gam/cmd/mobile.py +++ b/src/gam/cmd/mobile.py @@ -5,8 +5,9 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg -from gam.var import Cmd, Ent, Ind +from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject from gam.util.api_call import callGAPI, callGAPIpages, yieldGAPIpages from gam.util.args import ( diff --git a/src/gam/cmd/notes.py b/src/gam/cmd/notes.py index 1f9b459f..74585e07 100644 --- a/src/gam/cmd/notes.py +++ b/src/gam/cmd/notes.py @@ -9,6 +9,7 @@ import os from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/oauth.py b/src/gam/cmd/oauth.py index 6a5d0d5c..2a58ae15 100644 --- a/src/gam/cmd/oauth.py +++ b/src/gam/cmd/oauth.py @@ -11,6 +11,7 @@ import os import re import socket import sys +import datetime import time import webbrowser import wsgiref.simple_server diff --git a/src/gam/cmd/people.py b/src/gam/cmd/people.py index e53271bd..884e5543 100644 --- a/src/gam/cmd/people.py +++ b/src/gam/cmd/people.py @@ -51,6 +51,7 @@ import google.auth.exceptions from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind diff --git a/src/gam/cmd/printers.py b/src/gam/cmd/printers.py index d1c2f7f3..2135d9b1 100644 --- a/src/gam/cmd/printers.py +++ b/src/gam/cmd/printers.py @@ -6,6 +6,7 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import entityUnknownWarning diff --git a/src/gam/cmd/project.py b/src/gam/cmd/project.py index f3600e89..68ee820b 100644 --- a/src/gam/cmd/project.py +++ b/src/gam/cmd/project.py @@ -5,6 +5,7 @@ key operations, and API enablement. """ import base64 +import datetime import json import os import re @@ -288,7 +289,7 @@ def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e: sys.stderr.write(f'{str(e)}: {content}') return False - if 'error' not in content or 'error_description' not in content: + if not 'error' in content or not 'error_description' in content: sys.stderr.write(f'Unknown error: {content}\n') return False if content['error'] == 'invalid_grant': diff --git a/src/gam/cmd/reports.py b/src/gam/cmd/reports.py index 116b0b36..b4c418d2 100644 --- a/src/gam/cmd/reports.py +++ b/src/gam/cmd/reports.py @@ -2,13 +2,15 @@ import arrow +import datetime import re from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind from gam.util.access import accessErrorExit, entityUnknownWarning from gam.util.api import _getAdminEmail, buildGAPIObject from gam.util.api_call import callGAPI, callGAPIpages diff --git a/src/gam/cmd/reseller.py b/src/gam/cmd/reseller.py index 836300c2..78e6f832 100644 --- a/src/gam/cmd/reseller.py +++ b/src/gam/cmd/reseller.py @@ -3,6 +3,7 @@ import json import sys +import re from gamlib import api as API from gamlib import settings as GC diff --git a/src/gam/cmd/schemas.py b/src/gam/cmd/schemas.py index 4d0472e4..139954c8 100644 --- a/src/gam/cmd/schemas.py +++ b/src/gam/cmd/schemas.py @@ -4,6 +4,7 @@ from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import checkEntityAFDNEorAccessErrorExit diff --git a/src/gam/cmd/send_email.py b/src/gam/cmd/send_email.py index e8a2c4b0..34f0ec11 100644 --- a/src/gam/cmd/send_email.py +++ b/src/gam/cmd/send_email.py @@ -2,17 +2,23 @@ import time +import re from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind -from gam.util.api import _getAdminEmail +from gam.util.access import entityUnknownWarning +from gam.util.api import _getAdminEmail, buildGAPIObject from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIpages from gam.util.args import ( + FALSE, SORF_MSG_FILE_ARGUMENTS, + TRUE, + TRUE_FALSE, UTF8, checkArgumentPresent, getArgument, @@ -21,10 +27,12 @@ from gam.util.args import ( getDateOrDeltaFromNow, getEmailAddress, getFilename, + getREPatternSubstitution, getString, getStringOrFile, getTimeOrDeltaFromNow, normalizeEmailAddressOrUID, + splitEmailAddress, ) from gam.util.display import ( entityActionFailedWarning, @@ -35,13 +43,15 @@ from gam.util.display import ( userGmailServiceNotEnabledWarning, ) from gam.util.email import send_email -from gam.util.entity import getEntityArgument, getEntityList +from gam.util.entity import getEntityArgument, getEntityList, getEntityToModify, getNormalizedEmailAddressEntity from gam.util.errors import ( + invalidArgumentExit, + invalidChoiceExit, missingArgumentExit, unknownArgumentExit, usageErrorExit, ) -from gam.util.output import setSysExitRC +from gam.util.output import ERROR, setSysExitRC from gam.util.tags import ( # noqa: F401 # re-export ADDRESS_FIELDS_PRINT_ORDER, CASE_MARKERS, diff --git a/src/gam/cmd/sites.py b/src/gam/cmd/sites.py index 696ff41c..a7044eba 100644 --- a/src/gam/cmd/sites.py +++ b/src/gam/cmd/sites.py @@ -1,10 +1,12 @@ """GAM site verification and web resource management.""" import json +import sys from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject, getHttpObj diff --git a/src/gam/cmd/sso.py b/src/gam/cmd/sso.py index bd44ed35..57978e92 100644 --- a/src/gam/cmd/sso.py +++ b/src/gam/cmd/sso.py @@ -6,6 +6,7 @@ import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.api import buildGAPIObject diff --git a/src/gam/cmd/tasks.py b/src/gam/cmd/tasks.py index decc53de..7450a4ef 100644 --- a/src/gam/cmd/tasks.py +++ b/src/gam/cmd/tasks.py @@ -1,10 +1,12 @@ """GAM Google Tasks and Tag Manager management.""" import json +import sys from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.svcacct import buildGAPIServiceObject diff --git a/src/gam/cmd/userop/licenses.py b/src/gam/cmd/userop/licenses.py index bba12c59..0626f233 100644 --- a/src/gam/cmd/userop/licenses.py +++ b/src/gam/cmd/userop/licenses.py @@ -4,6 +4,8 @@ Part of the _userop_tmp sub-package.""" """GAM user operations: Looker Studio, user groups, licenses, photos, profile, sheets, tokens, deprovision.""" +import re +import sys from gam.cmd.userop.usergroups import LICENSE_PREVIEW_TITLES @@ -12,6 +14,7 @@ from gam.cmd.userop.usergroups import LICENSE_PRODUCT_SKUIDS from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gamlib import skus as SKU from gam.util.access import entityUnknownWarning diff --git a/src/gam/cmd/userop/photos.py b/src/gam/cmd/userop/photos.py index 077e883f..298d35e7 100644 --- a/src/gam/cmd/userop/photos.py +++ b/src/gam/cmd/userop/photos.py @@ -16,6 +16,7 @@ import google.auth.exceptions from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.access import entityUnknownWarning from gam.util.api import buildGAPIObject, getHttpObj @@ -45,7 +46,7 @@ from gam.util.output import writeStdout from gam.util.tags import _substituteForUser from gam.cmd.drive.core import _validateUserGetFileIDs, getDriveFileEntity -from gam.var import Cmd, Ent +from gam.var import Act, Cmd, Ent, Ind from tempfile import TemporaryFile diff --git a/src/gam/cmd/userop/sheets.py b/src/gam/cmd/userop/sheets.py index 9aebe4b7..f5fc1d36 100644 --- a/src/gam/cmd/userop/sheets.py +++ b/src/gam/cmd/userop/sheets.py @@ -4,11 +4,13 @@ Part of the _userop_tmp sub-package.""" """GAM user operations: Looker Studio, user groups, licenses, photos, profile, sheets, tokens, deprovision.""" +import re import json from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.svcacct import buildGAPIServiceObject from gam.util.api_call import callGAPI, callGAPIitems diff --git a/src/gam/cmd/userop/tokens.py b/src/gam/cmd/userop/tokens.py index 14177897..401bf702 100644 --- a/src/gam/cmd/userop/tokens.py +++ b/src/gam/cmd/userop/tokens.py @@ -11,6 +11,7 @@ from gam.cmd.userop.sheets import commonClientIds from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.access import ClientAPIAccessDeniedExit, entityUnknownWarning from gam.util.api import _getAdminEmail, buildGAPIObject diff --git a/src/gam/cmd/users/display.py b/src/gam/cmd/users/display.py index 263b67e8..5a23d247 100644 --- a/src/gam/cmd/users/display.py +++ b/src/gam/cmd/users/display.py @@ -34,7 +34,7 @@ from gamlib import gapi as GAPI from gamlib import state as GM from gamlib import msgs as Msg -from gam.var import Cmd, Ent, Ind +from gam.var import Act, Cmd, Ent, Ind from gamlib import skus as SKU from gamlib import uprop as UProp diff --git a/src/gam/cmd/users/manage.py b/src/gam/cmd/users/manage.py index 474f3253..e22460c2 100644 --- a/src/gam/cmd/users/manage.py +++ b/src/gam/cmd/users/manage.py @@ -5,6 +5,7 @@ Part of the _users_tmp sub-package.""" """GAM user management.""" import re +import sys from gam.util.args import DEFAULT_CHOICE @@ -106,7 +107,7 @@ def hashPassword(password): Returns a tuple of (hashed_password, hash_function_name). The hash_function_name is always 'crypt' for Google's Directory API. """ - return (sha512_crypt.using(rounds=10000).hash(password), 'crypt') + return (sha512_crypt.hash(password, rounds=10000), 'crypt') def _getGroupOrgUnitMap(): @@ -138,7 +139,7 @@ def _getGroupOrgUnitMap(): closeFile(f) return groupOrgUnitMap -class PasswordOptions: +class PasswordOptions(): def __init__(self, updateCmd): self.password = '' self.notFoundPassword = '' diff --git a/src/gam/cmd/userservices.py b/src/gam/cmd/userservices.py index 5c93739f..d954dc28 100644 --- a/src/gam/cmd/userservices.py +++ b/src/gam/cmd/userservices.py @@ -1,6 +1,7 @@ """GAM user service management: ASPs, backup codes, user calendars.""" import json +import sys from gam.util.args import formatLocalTimestamp @@ -21,6 +22,7 @@ from gam.cmd.calendar import ( from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Act, Cmd, Ent, Ind from gam.util.access import checkEntityAFDNEorAccessErrorExit, entityUnknownWarning @@ -84,6 +86,22 @@ from gam.util.display import ( userYouTubeServiceNotEnabledWarning, ) from gam.util.entity import ( + APPLICATION_VND_GOOGLE_APPS, + ME_IN_OWNERS, + ME_IN_OWNERS_AND, + MIMETYPE_GA_3P_SHORTCUT, + MIMETYPE_GA_DOCUMENT, + MIMETYPE_GA_FOLDER, + MIMETYPE_GA_FORM, + MIMETYPE_GA_PRESENTATION, + MIMETYPE_GA_SHORTCUT, + MIMETYPE_GA_SPREADSHEET, + NOT_ME_IN_OWNERS, + NOT_ME_IN_OWNERS_AND, + QUERY_SHORTCUTS_MAP, + SHAREDDRIVE_QUERY_SHORTCUTS_MAP, + _getEntityMimeType, + _getTargetEntityMimeType, checkUserSuspended, convertEntityToList, convertUIDtoEmailAddress, diff --git a/src/gam/cmd/vault/holds.py b/src/gam/cmd/vault/holds.py index 89c59a8d..f1aaf19b 100644 --- a/src/gam/cmd/vault/holds.py +++ b/src/gam/cmd/vault/holds.py @@ -4,6 +4,7 @@ Part of the _vault_tmp sub-package.""" """GAM Google Vault management.""" +import re import json from gam.cmd.vault.matters import _buildVaultQuery, _validateVaultQuery, convertHoldNameToID, convertMatterNameToID, convertQueryNameToID, formatVaultNameId, getMatterItem, warnMatterNotOpen @@ -18,7 +19,9 @@ from gam.cmd.vault.matters import ( import time from gamlib import api as API +from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.constants import NO_ENTITIES_FOUND_RC, PROJECTION_CHOICE_MAP diff --git a/src/gam/cmd/vault/matters.py b/src/gam/cmd/vault/matters.py index 95a18779..6b97cfbf 100644 --- a/src/gam/cmd/vault/matters.py +++ b/src/gam/cmd/vault/matters.py @@ -14,6 +14,7 @@ import time from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from gamlib import msgs as Msg from gam.util.api import buildGAPIObject from gam.util.api_call import callGAPI, callGAPIpages @@ -527,7 +528,7 @@ def doCreateVaultExport(): body['exportOptions'][optionsField] = {'includeAccessInfo': includeAccessInfo} else: if exportFormat is not None: - if exportFormat not in VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']]: + if not exportFormat in VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']]: Cmd.SetLocation(formatLocation) invalidChoiceExit(exportFormat, VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']], False) else: diff --git a/src/gam/util/access.py b/src/gam/util/access.py index a34cfa47..f2c9f661 100644 --- a/src/gam/util/access.py +++ b/src/gam/util/access.py @@ -4,6 +4,7 @@ Access-error diagnostics, API access denied handlers, and entity warning functions. """ +import sys from gamlib import action from gamlib import api as API @@ -14,11 +15,12 @@ from gamlib import state as GM from gamlib import indent from gamlib import msgs as Msg from gam.constants import API_ACCESS_DENIED_RC, INVALID_DOMAIN_RC -from util.api import _getAdminEmail, _getSvcAcctData, buildGAPIObject, APIAccessDeniedExit, ClientAPIAccessDeniedExit, SvcAcctAPIAccessDeniedExit, SvcAcctAPIDisabledExit # noqa: F401 +from util.api import _getAdminEmail, _getSvcAcctData, buildGAPIObject, APIAccessDeniedExit, ClientAPIAccessDeniedExit, SvcAcctAPIAccessDeniedExit, SvcAcctAPIDisabledExit from util.api_call import callGAPI from util.args import getEmailAddressDomain, getPhraseDNEorSNA from util.display import ENTITY_DOES_NOT_EXIST_RC, ENTITY_DUPLICATE_RC, entityActionFailedWarning, entityDoesNotExistWarning, entityServiceNotApplicableWarning -from util.output import currentCountNL, formatKeyValueList, setSysExitRC, systemErrorExit, writeStderr +from util.errors import OAUTH2SERVICE_JSON_REQUIRED_RC +from util.output import currentCountNL, formatKeyValueList, setSysExitRC, stderrErrorMsg, systemErrorExit, writeStderr diff --git a/src/gam/util/api.py b/src/gam/util/api.py index 577b62d6..838a5843 100644 --- a/src/gam/util/api.py +++ b/src/gam/util/api.py @@ -43,16 +43,18 @@ except ImportError: from gamlib import api as API from gamlib import settings as GC +from gamlib import gapi as GAPI +from gamlib import gdata as GDATA from gamlib import state as GM from gamlib import msgs as Msg from gamlib import yubikey from gam.var import Ent, Ind -from gam.constants import API_ACCESS_DENIED_RC, GOOGLE_API_ERROR_RC, NETWORK_ERROR_RC, NO_SCOPES_FOR_API_RC, REFRESH_EXPIRY, SOCKET_ERROR_RC, SYSTEM_ERROR_RC -from util.args import UTF8, YYYYMMDDTHHMMSSZ_FORMAT -from util.display import SERVICE_NOT_APPLICABLE_RC, entityActionFailedWarning, printBlankLine, printKeyValueList, userServiceNotEnabledWarning +from gam.constants import API_ACCESS_DENIED_RC, GOOGLE_API_ERROR_RC, HTTP_ERROR_RC, NETWORK_ERROR_RC, NO_SCOPES_FOR_API_RC, REFRESH_EXPIRY, SOCKET_ERROR_RC, SYSTEM_ERROR_RC +from util.args import UTF8, YYYYMMDDTHHMMSSZ_FORMAT, formatHTTPError +from util.display import FIRST_ITEM_MARKER, LAST_ITEM_MARKER, SERVICE_NOT_APPLICABLE_RC, TOTAL_ITEMS_MARKER, entityActionFailedWarning, printBlankLine, printKeyValueList, userServiceNotEnabledWarning from util.errors import INVALID_JSON_RC, OAUTH2SERVICE_JSON_REQUIRED_RC, OAUTH2_TXT_REQUIRED_RC, expiredRevokedOauth2TxtExit, invalidDiscoveryJsonExit, invalidOauth2TxtExit, invalidOauth2serviceJsonExit -from util.fileio import FILE_ERROR_RC, UNKNOWN, incrAPICallsRetryData, readFile, writeFile -from util.output import flushStderr, setSysExitRC, stderrErrorMsg, systemErrorExit, writeStderr, writeStdout +from util.fileio import FILE_ERROR_RC, UNKNOWN, checkAPICallsRate, incrAPICallsRetryData, readFile, writeFile +from util.output import ERROR_PREFIX, flushStderr, setSysExitRC, stderrErrorMsg, systemErrorExit, writeStderr, writeStdout HTML_TITLE_PATTERN = re.compile(r'.*(.+)') diff --git a/src/gam/util/api_call.py b/src/gam/util/api_call.py index 2eef48ae..c639e1dd 100644 --- a/src/gam/util/api_call.py +++ b/src/gam/util/api_call.py @@ -8,6 +8,9 @@ construction) to break the api<->uid circular dependency. import http.client import json import re +import ssl +import sys +import time import googleapiclient.errors import httplib2 @@ -19,12 +22,13 @@ from gamlib import gdata as GDATA from gamlib import state as GM from gamlib import msgs as Msg from gam.var import Ent -from gam.constants import GOOGLE_API_ERROR_RC, HTTP_ERROR_RC, NETWORK_ERROR_RC, SOCKET_ERROR_RC -from util.api import APIAccessDeniedExit, clearServiceCache, getGDataOAuthToken, handleOAuthTokenError, handleServerError, transportCreateRequest, waitOnFailure +from gam.constants import GOOGLE_API_ERROR_RC, HTTP_ERROR_RC, NETWORK_ERROR_RC, SOCKET_ERROR_RC, SYSTEM_ERROR_RC +from util.api import APIAccessDeniedExit, clearServiceCache, getGDataOAuthToken, getHttpObj, handleOAuthTokenError, handleServerError, transportCreateRequest, waitOnFailure from util.args import UTF8, formatHTTPError -from util.display import FIRST_ITEM_MARKER, LAST_ITEM_MARKER, TOTAL_ITEMS_MARKER -from util.fileio import checkAPICallsRate -from util.output import ERROR_PREFIX, flushStderr, stderrErrorMsg, systemErrorExit, writeStderr, writeStdout +from util.display import FIRST_ITEM_MARKER, LAST_ITEM_MARKER, SERVICE_NOT_APPLICABLE_RC, TOTAL_ITEMS_MARKER, entityActionFailedWarning, printKeyValueList, userServiceNotEnabledWarning +from util.errors import INVALID_JSON_RC +from util.fileio import checkAPICallsRate, incrAPICallsRetryData +from util.output import ERROR_PREFIX, flushStderr, setSysExitRC, stderrErrorMsg, systemErrorExit, writeStderr, writeStdout HTML_TITLE_PATTERN = re.compile(r'.*(.+)') diff --git a/src/gam/util/args.py b/src/gam/util/args.py index 0afcf4eb..928adb02 100644 --- a/src/gam/util/args.py +++ b/src/gam/util/args.py @@ -72,9 +72,12 @@ __all__ = [ 'validateREPattern', 'validateREPatternSubstitution', ] +import calendar +import datetime import json import re import shlex +import sys import arrow @@ -828,7 +831,7 @@ def getNumberRangeList(): SORTORDER_CHOICE_MAP = {'ascending': 'ASCENDING', 'descending': 'DESCENDING'} -class OrderBy: +class OrderBy(): def __init__(self, choiceMap, ascendingKeyword='', descendingKeyword='desc'): self.choiceMap = choiceMap self.ascendingKeyword = ascendingKeyword @@ -1290,7 +1293,7 @@ def mapQueryRelativeTimes(query, keywords): pos = mg.end() return query -class StartEndTime: +class StartEndTime(): def __init__(self, startkw='starttime', endkw='endtime', mode='time'): self.startTime = self.endTime = self.startDateTime = self.endDateTime = None self._startkw = startkw diff --git a/src/gam/util/config.py b/src/gam/util/config.py index 7940a727..a43310ea 100644 --- a/src/gam/util/config.py +++ b/src/gam/util/config.py @@ -6,11 +6,14 @@ Reads gam.cfg and initializes all global runtime state. import collections import configparser import codecs +import datetime import http.client import json +import locale import os import re import sys +import time import httplib2 import arrow @@ -27,6 +30,7 @@ from util.args import getRowFilterDateOrDeltaFromNow from util.args import getRowFilterTimeOrDeltaFromNow from util.args import integerLimits from util.args import LOCALE_CODES_MAP +from util.args import LANGUAGE_CODES_MAP from util.args import TIMEZONE_FORMAT_REQUIRED from util.args import ( checkArgumentPresent, getArgument, getBoolean, getCharacter, getChoice, @@ -53,7 +57,7 @@ from util.output import ( from gam.constants import ( CONFIG_ERROR_RC, DEFAULT_FILE_APPEND_MODE, DEFAULT_FILE_READ_MODE, DEFAULT_FILE_WRITE_MODE, EV_GAMCFGDIR, EV_GAMCFGSECTION, EV_OLDGAMPATH, - FN_GAM_CFG, + FN_GAM_CFG, GAM, ) REGEX_CHARS = '^$*+|$[{(' @@ -71,7 +75,7 @@ MULTIPROCESS_EXIT_RANGE_PATTERN = re.compile(r'^rcrange(=|!=)(\S+)/(\S+)$', re.I def _stringInQuotes(value): - return (len(value) > 1) and (((value.startswith('"') and value.endswith('"'))) or (value.startswith("'") and value.endswith("'"))) + return (len(value) > 1) and (((value.startswith('"') and value.endswith('"'))) or ((value.startswith("'") and value.endswith("'")))) def _stripStringQuotes(value): if _stringInQuotes(value): diff --git a/src/gam/util/connection.py b/src/gam/util/connection.py index c28f525d..2ba3c347 100644 --- a/src/gam/util/connection.py +++ b/src/gam/util/connection.py @@ -1,5 +1,6 @@ """Network diagnostics, version display, and usage functions.""" +import logging import os import platform import socket @@ -308,7 +309,7 @@ def doVersion(checkForArgs=True): if simple: writeStdout(__version__) return - writeStdout(f'{GAM} {__version__} - {GAM_URL} - {GM.Globals[GM.GAM_TYPE]}\n' + writeStdout((f'{GAM} {__version__} - {GAM_URL} - {GM.Globals[GM.GAM_TYPE]}\n' f'{__author__}\n' f'Python {sys.version_info[0]}.{sys.version_info[1]}.{sys.version_info[2]} {struct.calcsize("P")*8}-bit {sys.version_info[3]}\n' f'{getOSPlatform()} {platform.machine()}\n' @@ -316,7 +317,7 @@ def doVersion(checkForArgs=True): f'{Ent.Singular(Ent.CONFIG_FILE)}: {GM.Globals[GM.GAM_CFG_FILE]}, {Ent.Singular(Ent.SECTION)}: {GM.Globals[GM.GAM_CFG_SECTION_NAME]}, ' f'{GC.CUSTOMER_ID}: {GC.Values[GC.CUSTOMER_ID]}, {GC.DOMAIN}: {GC.Values[GC.DOMAIN]}\n' f'Time: {ISOformatTimeStamp(todaysTime())}\n' - ) + )) if sys.platform.startswith('win') and str(struct.calcsize('P')*8).find('32') != -1 and platform.machine().find('64') != -1: printKeyValueList([Msg.UPDATE_GAM_TO_64BIT]) if timeOffset: diff --git a/src/gam/util/csv_pf.py b/src/gam/util/csv_pf.py index a59bda25..efdc753a 100644 --- a/src/gam/util/csv_pf.py +++ b/src/gam/util/csv_pf.py @@ -320,7 +320,7 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter, return True return False for column in columns: - if str(row.get(column, '')) not in filterData: + if not str(row.get(column, '')) in filterData: return False return True @@ -331,7 +331,7 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter, return False return True for column in columns: - if str(row.get(column, '')) not in filterData: + if not str(row.get(column, '')) in filterData: return True return False @@ -494,7 +494,7 @@ def getItemFieldsFromFieldsList(item, fieldsList, returnItemIfNoneList=False): return None return f'nextPageToken,{item}' -class CSVPrintFile: +class CSVPrintFile(): def __init__(self, titles=None, sortTitles=None, indexedTitles=None): self.rows = [] @@ -718,6 +718,7 @@ class CSVPrintFile: usageErrorExit(Msg.INVALID_ENTITY.format(Ent.Singular(entityType), message)) def getDriveObject(): + from gam.cmd.drive.core import escapeDriveFileName if not GC.Values[GC.TODRIVE_CLIENTACCESS]: _, drive = buildGAPIServiceObject(chooseSaAPI(API.DRIVETD, API.DRIVE3), self.todrive['user']) if not drive: @@ -1923,7 +1924,7 @@ def showJSON(showName, showValue, skipObjects=None, timeObjects=None, dictObjectsKey = dictObjectsKey or {} _show(showName, showValue, None, None, 0, DEFAULT_SKIP_OBJECTS.union(skipObjects or set())) -class FormatJSONQuoteChar: +class FormatJSONQuoteChar(): def __init__(self, csvPF=None, formatJSONOnly=False): self.SetCsvPF(csvPF) diff --git a/src/gam/util/display.py b/src/gam/util/display.py index 9138a8e3..edb28da9 100644 --- a/src/gam/util/display.py +++ b/src/gam/util/display.py @@ -5,6 +5,7 @@ and messages for user-facing output. Depends only on gamlib modules and util.output. """ +import sys from gamlib import settings as GC from gamlib import state as GM diff --git a/src/gam/util/email.py b/src/gam/util/email.py index e46a0b0b..cb9a8042 100644 --- a/src/gam/util/email.py +++ b/src/gam/util/email.py @@ -8,6 +8,7 @@ import mimetypes import os import smtplib import ssl +import sys from email.mime.application import MIMEApplication from email.mime.audio import MIMEAudio diff --git a/src/gam/util/entity.py b/src/gam/util/entity.py index a9f8ffd0..2b9a520a 100644 --- a/src/gam/util/entity.py +++ b/src/gam/util/entity.py @@ -4,9 +4,11 @@ UID-to-email conversion, entity list expansion, group member checking, entity selectors, and customer ID helpers. """ +import csv import os import platform import re +import shlex import sys import warnings @@ -126,9 +128,9 @@ from util.args import shlexSplitList, shlexSplitListStatus # noqa: E402,F401 - from util.uid import convertUIDtoEmailAddress, convertUIDtoEmailAddressWithType, convertEmailAddressToUID # noqa: F401 - re-export from gam.constants import DATA_ERROR_RC, INVALID_ENTITY_RC, NO_ENTITIES_FOUND_RC, UNKNOWN_ERROR_RC from gamlib import skus as SKU -from util.args import ARCHIVED_ARGUMENTS, FALSE_VALUES, SUSPENDED_ARGUMENTS, TRUE_VALUES, _getIsArchived, _getIsSuspended, checkArgumentPresent, checkDataField, checkMatchSkipFields, checkSubkeyField, getArgument, getCharSet, getChoice, getDelimiter, getMatchSkipFields, getREPattern, getString, makeOrgUnitPathAbsolute, normalizeEmailAddressOrUID, orgUnitPathQuery, removeCourseIdScope, splitEmailAddress, validateEmailAddressOrUID +from util.args import ARCHIVED_ARGUMENTS, FALSE_VALUES, SUSPENDED_ARGUMENTS, TRUE_VALUES, _getIsArchived, _getIsSuspended, checkArgumentPresent, checkDataField, checkMatchSkipFields, checkSubkeyField, getArgument, getCharSet, getChoice, getDelimiter, getMatchSkipFields, getPhraseDNEorSNA, getREPattern, getString, makeOrgUnitPathAbsolute, normalizeEmailAddressOrUID, orgUnitPathQuery, removeCourseIdScope, splitEmailAddress, validateEmailAddressOrUID from util.display import ENTITY_DOES_NOT_EXIST_RC, entityActionFailedWarning, entityActionNotPerformedWarning, entityDoesNotExistWarning, entityPerformActionNumItems, getPageMessage, getPageMessageForWhom, printGettingAllAccountEntities, printGettingAllEntityItemsForWhom, printGotEntityItemsForWhom, setGettingAllEntityItemsForWhom -from util.errors import csvDataAlreadySavedErrorExit, csvFieldErrorExit, invalidArgumentExit, invalidChoiceExit, missingArgumentExit, usageErrorExit +from util.errors import csvDataAlreadySavedErrorExit, csvFieldErrorExit, entityDoesNotExistExit, invalidArgumentExit, invalidChoiceExit, missingArgumentExit, usageErrorExit from util.fileio import closeFile, openFile, setFilePath from util.gdoc import getGDocData, getStorageFileData, openCSVFileReader from util.output import formatKeyValueList, printErrorMessage, setSysExitRC, stderrErrorMsg, systemErrorExit, writeStderr @@ -457,6 +459,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA _addGroupUsersToUsers(member['email'], domains, recursive, includeDerivedMembership) def _addCIGroupUsersToUsers(groupName, groupEmail, recursive): + from gam.cmd.licenses import doPrintLicenses + from gam.cmd.courses.courses import _getCoursesOwnerInfo printGettingAllEntityItemsForWhom(memberRoles if memberRoles else Ent.ROLE_MANAGER_MEMBER_OWNER, groupEmail, entityType=Ent.CLOUD_IDENTITY_GROUP) validRoles = _getCIRoleVerification(memberRoles) try: diff --git a/src/gam/util/gdoc.py b/src/gam/util/gdoc.py index ea323fc8..6cbe9737 100644 --- a/src/gam/util/gdoc.py +++ b/src/gam/util/gdoc.py @@ -235,6 +235,7 @@ def getStorageFileData(gcsformat, returnData=True): # def openCSVFileReader(filename, fieldnames=None): + from gam.cmd.drive.transfer.fileops import HTTP_ERROR_PATTERN filenameLower = filename.lower() if filenameLower == 'gsheet': f = getGSheetData() diff --git a/src/gam/util/orgunits.py b/src/gam/util/orgunits.py index 6e43cd9a..34d96132 100644 --- a/src/gam/util/orgunits.py +++ b/src/gam/util/orgunits.py @@ -3,10 +3,12 @@ OrgUnit path/ID resolution and parent OrgUnit traversal. """ +import sys from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM from util.access import accessErrorExit, checkEntityAFDNEorAccessErrorExit from util.api import buildGAPIObject from util.api_call import callGAPI diff --git a/src/gam/util/svcacct.py b/src/gam/util/svcacct.py index 63866869..f5a6b8e0 100644 --- a/src/gam/util/svcacct.py +++ b/src/gam/util/svcacct.py @@ -11,6 +11,7 @@ import time import google.auth.exceptions import httplib2 +from gamlib import api as API from gamlib import settings as GC from gamlib import state as GM from gam.constants import NETWORK_ERROR_RC diff --git a/src/gam/util/uid.py b/src/gam/util/uid.py index f1947eb9..e4cb8beb 100644 --- a/src/gam/util/uid.py +++ b/src/gam/util/uid.py @@ -7,10 +7,15 @@ Extracted from entity.py to break the entity↔api circular dependency. from gamlib import api as API from gamlib import settings as GC from gamlib import gapi as GAPI +from gamlib import state as GM +from gamlib import msgs as Msg from gam.var import Ent from util.api import buildGAPIObject from util.api_call import callGAPI from util.args import normalizeEmailAddressOrUID +from util.display import entityDoesNotExistWarning, printGettingAllEntityItemsForWhom +from util.errors import usageErrorExit +from util.output import currentCountNL NON_EMAIL_MEMBER_PREFIXES = ( "cbcm-browser.",