print user counts by ou, upload message attachments

This commit is contained in:
Ross Scroggs
2024-05-03 17:52:54 -07:00
parent ab5aa02bf8
commit a8938f84f0
10 changed files with 226 additions and 27 deletions

View File

@@ -10,6 +10,19 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation
### 6.75.04
Added a command to print user counts by OrgUnit. By default, all users in the workspace are counted;
you can specify a domain to only count users in that domain.
```
gam print usercountsbyorgunit [todrive <ToDriveAttribute>*]
[domain <String>]
```
Added option `uploadattachments [<DriveFileParentAttribute>]` to `gam <UserTypeEntity> show messages|threads` that
causes GAM to upload all message attachments to the user's `My Drive`, the default, or to a specific folder.
The existing option `attachmentnamepattern <RegularExpression>` can be used to select attachments to upload.
### 6.75.03 ### 6.75.03
Fixed bug in `gam batch|tbatch` where the line `sleep <Integer>` in the batch file caused the error: Fixed bug in `gam batch|tbatch` where the line `sleep <Integer>` in the batch file caused the error:

View File

@@ -335,7 +335,7 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64 MacOS Sonoma 14.4.1 x86_64
@@ -1009,7 +1009,7 @@ writes the credentials into the file oauth2.txt.
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
C:\GAMADV-XTD3>gam version C:\GAMADV-XTD3>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
Windows-10-10.0.17134 AMD64 Windows-10-10.0.17134 AMD64

View File

@@ -23,6 +23,7 @@
- [Print only options](#print-only-options) - [Print only options](#print-only-options)
- [Show only options](#show-only-options) - [Show only options](#show-only-options)
- [Download attachments](#download-attachments) - [Download attachments](#download-attachments)
- [Upload attachments](#upload-attachments)
- [Display messages sent by delegates for delegator](#display-messages-sent-by-delegates-for-delegator) - [Display messages sent by delegates for delegator](#display-messages-sent-by-delegates-for-delegator)
- [User attribute `replace <Tag> <UserReplacement>` processing](Tag-Replace) - [User attribute `replace <Tag> <UserReplacement>` processing](Tag-Replace)
@@ -180,6 +181,20 @@ Block emails between specific user groups
(gdoc|ghtml <UserGoogleDoc>)| (gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)| (gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName> [charset <Charset>])) (emlfile <FileName> [charset <Charset>]))
<DriveFolderID> ::= <String>
<DriveFolderName> ::= <String>
<SharedDriveID> ::= <String>
<SharedDriveName> ::= <String>
<DriveFileParentAttribute> ::=
(parentid <DriveFolderID>)|
(parentname <DriveFolderName>)|
(anyownerparentname <DriveFolderName>)|
(teamdriveparentid <DriveFolderID>)|
(teamdriveparent <SharedDriveName>)|
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
``` ```
## Message queries with dates ## Message queries with dates
``` ```
@@ -595,6 +610,16 @@ By default, when downloading attachments, an existing local file will not be ove
* `overwrite true` - Overwite an existing file * `overwrite true` - Overwite an existing file
* `overwrite false` - Do not overwite an existing file; add a numeric prefix and create a new file * `overwrite false` - Do not overwite an existing file; add a numeric prefix and create a new file
## Upload attachments
These options are valid with `show'.
By default, message attachments are not uploaded to Google Drive.
* `uploadattachments` - Upload message attachments
* `attachmentnamepattern <RegularExpression>` - Limit the attachments uploaded to those whose names match `<RegularExpression>`
By default, message attachments are uploaded to the root of the user's My Drive.
* `<DriveFileParentAttributeh>` - Specify an alternate location for the uploaded attachments
## Display messages sent by delegates for delegator ## Display messages sent by delegates for delegator
Display messages sent by a particular delegate for a delegator; the message is Display messages sent by a particular delegate for a delegator; the message is
from the delegator but sent by the delegate. from the delegator but sent by the delegate.

View File

@@ -37,6 +37,7 @@
- [Print user domain counts](#print-user-domain-counts) - [Print user domain counts](#print-user-domain-counts)
- [Print domain counts for users in a specific domain and/or selected by a query](#print-domain-counts-for-users-in-a-specific-domain-and-or-selected-by-a-query) - [Print domain counts for users in a specific domain and/or selected by a query](#print-domain-counts-for-users-in-a-specific-domain-and-or-selected-by-a-query)
- [Print domain counts for users specified by `<UserTypeEntity>`](#print-domain-counts-for-users-specified-by-usertypeentity) - [Print domain counts for users specified by `<UserTypeEntity>`](#print-domain-counts-for-users-specified-by-usertypeentity)
- [Print user counts by OrgUnit](print-user-counts-by-orgunit)
- [Print user list](#print-user-list) - [Print user list](#print-user-list)
- [Display user counts](#display-user-counts) - [Display user counts](#display-user-counts)
- [Verify domain membership]($verify-domain-membership) - [Verify domain membership]($verify-domain-membership)
@@ -1242,6 +1243,15 @@ $ more UsersList.csv
["testuser1@domain.org", "testuser2@domain.org", "testuser3@domain.org", "testuser4@domain.org"] ["testuser1@domain.org", "testuser2@domain.org", "testuser3@domain.org", "testuser4@domain.org"]
``` ```
## Print user counts by OrgUnit
Display the count of archived, suspended and total users in each OrgUnit; display a grand total.
By default, all users in the workspace are counted; you can specify a domain to only count users in that domain.
```
gam print usercountsbyorgunit [todrive <ToDriveAttribute>*]
[domain <String>]
```
## Display user counts ## Display user counts
Display the number of users in an entity. Display the number of users in an entity.
``` ```

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details Print the current version of Gam with details
``` ```
gam version gam version
GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64 MacOS Sonoma 14.4.1 x86_64
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information Print the current version of Gam with details and time offset information
``` ```
gam version timeoffset gam version timeoffset
GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64 MacOS Sonoma 14.4.1 x86_64
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
Print the current version of Gam with extended details and SSL information Print the current version of Gam with extended details and SSL information
``` ```
gam version extended gam version extended
GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64 MacOS Sonoma 14.4.1 x86_64
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3 Path: /Users/Admin/bin/gamadv-xtd3
Version Check: Version Check:
Current: 5.35.08 Current: 5.35.08
Latest: 6.75.03 Latest: 6.75.04
echo $? echo $?
1 1
``` ```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details Print the current version number without details
``` ```
gam version simple gam version simple
6.75.03 6.75.04
``` ```
In Linux/MacOS you can do: In Linux/MacOS you can do:
``` ```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki Print the current version of Gam and address of this Wiki
``` ```
gam help gam help
GAM 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 GAM 6.75.04 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.3 64-bit final Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64 MacOS Sonoma 14.4.1 x86_64

View File

@@ -5529,6 +5529,11 @@ gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]] [countsonly|countonly] [formatjson [quotechar <Character>]] [countsonly|countonly]
[issuspended <Boolean>] [issuspended <Boolean>]
Print user counts by OrgUnits
gam print usercountsbyorgunit [todrive <ToDriveAttribute>*]
[domain <String>]
Print lists of users Print lists of users
gam <UserTypeEntity> print userlist [todrive <ToDriveAttribute>*] gam <UserTypeEntity> print userlist [todrive <ToDriveAttribute>*]
@@ -7202,9 +7207,10 @@ gam <UserTypeEntity> show messages|threads
[countsonly|positivecountsonly] [useronly] [countsonly|positivecountsonly] [useronly]
[headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]] [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
[showlabels] [delimiter <Character>] [showbody] [showdate] [showsize] [showsnippet] [showlabels] [delimiter <Character>] [showbody] [showdate] [showsize] [showsnippet]
[showattachments [attachmentnamepattern <RegularExpression>] [noshowtextplain]] [[attachmentnamepattern <RegularExpression>]
[saveattachments [attachmentnamepattern <RegularExpression>]] [showattachments [noshowtextplain]]
[targetfolder <FilePath>] [overwrite [<Boolean>]] [saveattachments [targetfolder <FilePath>] [overwrite [<Boolean>]]]
[uploadattachments [<DriveFileParentAttribute>]]]
gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*] gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*]
(((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])* (((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])*
[quick|notquick] [max_to_print <Number>] [includespamtrash])|(ids <MessageIDEntity>) [quick|notquick] [max_to_print <Number>] [includespamtrash])|(ids <MessageIDEntity>)
@@ -7212,7 +7218,8 @@ gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*]
[countsonly|positivecountsonly] [useronly] [countsonly|positivecountsonly] [useronly]
[headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String> [dateheaderconverttimezone [<Boolean>]]] [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String> [dateheaderconverttimezone [<Boolean>]]]
[showlabels] [delimiter <Character>] [showbody] [showdate] [showsize] [showsnippet] [showlabels] [delimiter <Character>] [showbody] [showdate] [showsize] [showsnippet]
[showattachments [attachmentnamepattern <RegularExpression>]] [[attachmentnamepattern <RegularExpression>]
[showattachments [noshowtextplain]]]
[convertcrnl] [convertcrnl]
# Users - Gmail - Profile # Users - Gmail - Profile

View File

@@ -2,6 +2,19 @@
Merged GAM-Team version Merged GAM-Team version
6.75.04
Added a command to print user counts by OrgUnit. By default, all users in the workspace are counted;
you can specify a domain to only count users in that domain.
```
gam print usercountsbyorgunit [todrive <ToDriveAttribute>*]
[domain <String>]
```
Added option `uploadattachments [<DriveFileParentAttribute>]` to `gam <UserTypeEntity> show messages|threads` that
causes GAM to upload all message attachments to the user's `My Drive`, the default, or to a specific folder.
The existing option `attachmentnamepattern <RegularExpression>` can be used to select attachments to upload.
6.75.03 6.75.03
Fixed bug in `gam batch|tbatch` where the line `sleep <Integer>` in the batch file caused the error: Fixed bug in `gam batch|tbatch` where the line `sleep <Integer>` in the batch file caused the error:

View File

@@ -11659,6 +11659,25 @@ def doCreateProject():
elif 'error' in status: elif 'error' in status:
systemErrorExit(2, status['error']+'\n') systemErrorExit(2, status['error']+'\n')
break break
# Try to set policy on project to allow Service Account Key Upload
# orgp = getAPIService(API.ORGPOLICY, httpObj)
# projectParent = f"projects/{projectInfo['projectId']}"
# policyName = f'{projectParent}/policies/iam.disableServiceAccountKeyUpload'
# try:
# result = callGAPI(orgp.projects().policies(), 'get',
# throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED],
# name=policyName)
# if result['spec']['rules'][0]['enforce']:
# callGAPI(orgp.projects().policies(), 'patch',
# throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED],
# name=policyName, body={'spec': {'rules': [{'enforce': False}]}}, updateMask='policy.spec')
# except GAPI.notFound:
# callGAPI(orgp.projects().policies(), 'create',
# throwReasons=[GAPI.BAD_REQUEST, GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED],
# parent=projectParent, body={'name': policyName, 'spec': {'rules': [{'enforce': False}]}})
# except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.permissionDenied):
# pass
# Create client_secrets.json and oauth2service.json
_createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo) _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo)
# gam use project [<EmailAddress>] [<ProjectID>] # gam use project [<EmailAddress>] [<ProjectID>]
@@ -43033,6 +43052,7 @@ def doPrintUsers(entityList=None):
_writeUserEntity(userEntity) _writeUserEntity(userEntity)
def _updateDomainCounts(emailAddress): def _updateDomainCounts(emailAddress):
nonlocal domainCounts
atLoc = emailAddress.find('@') atLoc = emailAddress.find('@')
if atLoc == -1: if atLoc == -1:
dom = UNKNOWN dom = UNKNOWN
@@ -43408,6 +43428,67 @@ def doPrintUserList(entityList):
csvPF.WriteRow({'title': title, 'count': count, 'users': json.dumps(cleanJSON(entityList), ensure_ascii=False, sort_keys=True)}) csvPF.WriteRow({'title': title, 'count': count, 'users': json.dumps(cleanJSON(entityList), ensure_ascii=False, sort_keys=True)})
csvPF.writeCSVfile('User List') csvPF.writeCSVfile('User List')
# gam print usercountsbyorgunit [todrive <ToDriveAttribute>*]
# [domain <String>]
def doPrintUserCountsByOrgUnit():
def _printUserCounts(title, v):
csvPF.WriteRow({'orgUnitPath': title, 'archived': v['archived'], 'active': v['active'], 'suspended': v['suspended'], 'total': v['total']})
USER_COUNTS_FIELDS = ['archived', 'active', 'suspended', 'total']
USER_COUNTS_ZERO_FIELDS = {'archived': 0, 'active': 0, 'suspended': 0, 'total': 0}
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['orgUnitPath']+USER_COUNTS_FIELDS)
FJQC = FormatJSONQuoteChar(csvPF)
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]}
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if myarg == 'todrive':
csvPF.GetTodriveParameters()
elif myarg == 'domain':
kwargs = {'domain': getString(Cmd.OB_DOMAIN_NAME)}
else:
FJQC.GetFormatJSONQuoteChar(myarg, False)
if 'domain' in kwargs:
printGettingAllEntityItemsForWhom(Ent.USER, kwargs['domain'], entityType=Ent.DOMAIN)
pageMessage = getPageMessageForWhom()
title = f"Total({kwargs['domain']})"
else:
printGettingAllAccountEntities(Ent.USER)
pageMessage = getPageMessage()
title = f"Total({kwargs['customer']})"
userCounts = {}
try:
result = callGAPIpages(cd.users(), 'list', 'users',
pageMessage=pageMessage,
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND, GAPI.FORBIDDEN],
orderBy='email', fields='nextPageToken,users(orgUnitPath,archived,suspended)',
maxResults=GC.Values[GC.USER_MAX_RESULTS], **kwargs)
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.domainNotFound):
if 'domain' in kwargs:
checkEntityDNEorAccessErrorExit(cd, Ent.DOMAIN, kwargs['domain'])
else:
checkEntityDNEorAccessErrorExit(cd, Ent.CUSTOMER_ID, kwargs['customer'])
return
for user in result:
orgUnitPath = user['orgUnitPath']
if orgUnitPath not in userCounts:
userCounts[orgUnitPath] = USER_COUNTS_ZERO_FIELDS.copy()
if user['suspended'] or user['archived']:
if user['archived']:
userCounts[orgUnitPath]['archived'] += 1
if user['suspended']:
userCounts[orgUnitPath]['suspended'] += 1
else:
userCounts[orgUnitPath]['active'] += 1
userCounts[orgUnitPath]['total'] += 1
totalCounts = USER_COUNTS_ZERO_FIELDS.copy()
for k, v in sorted(iter(userCounts.items())):
_printUserCounts(k, v)
for f in USER_COUNTS_FIELDS:
totalCounts[f] += v[f]
_printUserCounts(title, totalCounts)
csvPF.writeCSVfile('User Counts by OrgUnit')
def isolateCIUserInvitatonsEmail(name): def isolateCIUserInvitatonsEmail(name):
''' converts long name into email address''' ''' converts long name into email address'''
return name.split('/')[-1] return name.split('/')[-1]
@@ -67891,7 +67972,7 @@ def printShowMessagesThreads(users, entityType):
if charset: if charset:
printKeyValueList(['charset', charset]) printKeyValueList(['charset', charset])
def _showSaveAttachments(messageId, payload, attachmentNamePattern): def _showSaveAttachments(messageId, payload, attachmentNamePattern, j, jcount):
for part in payload.get('parts', []): for part in payload.get('parts', []):
if 'attachmentId' in part['body']: if 'attachmentId' in part['body']:
for header in part['headers']: for header in part['headers']:
@@ -67906,7 +67987,7 @@ def printShowMessagesThreads(users, entityType):
mg = CHARSET_NAME_PATTERN.match(header['value']) mg = CHARSET_NAME_PATTERN.match(header['value'])
if mg: if mg:
charset = mg.group(1) charset = mg.group(1)
if (part['mimeType'] == 'text/plain' and not noshow_text_plain) or save_attachments: if (part['mimeType'] == 'text/plain' and not noshow_text_plain) or save_attachments or upload_attachments:
try: try:
result = callGAPI(gmail.users().messages().attachments(), 'get', result = callGAPI(gmail.users().messages().attachments(), 'get',
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND], throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND],
@@ -67933,6 +68014,34 @@ def printShowMessagesThreads(users, entityType):
else: else:
entityActionFailedWarning([Ent.ATTACHMENT, filename], str(e)) entityActionFailedWarning([Ent.ATTACHMENT, filename], str(e))
Act.Set(action) Act.Set(action)
if upload_attachments:
filename = cleanFilename(attachmentName)
uploadAttachmentBody.update({'name': filename, 'mimeType': part['mimeType']})
action = Act.Get()
Act.Set(Act.CREATE)
media_body = googleapiclient.http.MediaIoBaseUpload(io.BytesIO(base64.urlsafe_b64decode(str(result['data']))), mimetype=part['mimeType'], resumable=True)
try:
result = callGAPI(drive.files(), 'create',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR, GAPI.INTERNAL_ERROR,
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP,
GAPI.UPLOAD_TOO_LARGE, GAPI.TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED],
media_body=media_body, body=uploadAttachmentBody, fields='id,name', supportsAllDrives=True)
entityModifierItemValueListActionPerformed([Ent.DRIVE_FILE, f"{result['name']}({result['id']})"],
Act.MODIFIER_WITH_CONTENT_FROM, [Ent.ATTACHMENT, filename], j, jcount)
except (GAPI.forbidden, GAPI.insufficientPermissions, GAPI.insufficientParentPermissions,
GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent,
GAPI.fileNotFound, GAPI.unknownError, GAPI.internalError,
GAPI.storageQuotaExceeded, GAPI.teamdrivesSharingRestrictionNotAllowed,
GAPI.teamdrivefileLimitExceeded, GAPI.teamdriveHierarchyTooDeep,
GAPI.uploadTooLarge, GAPI.teamdrivesShortcutFileNotSupported) as e:
entityModifierItemValueListActionFailedWarning([Ent.DRIVE_FILE, None],
Act.MODIFIER_WITH_CONTENT_FROM, [Ent.ATTACHMENT, filename], str(e), j, jcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
Act.Set(action)
except (GAPI.serviceNotAvailable, GAPI.badRequest, GAPI.notFound): except (GAPI.serviceNotAvailable, GAPI.badRequest, GAPI.notFound):
pass pass
elif show_attachments: elif show_attachments:
@@ -67942,7 +68051,7 @@ def printShowMessagesThreads(users, entityType):
Ind.Decrement() Ind.Decrement()
break break
else: else:
_showSaveAttachments(messageId, part, attachmentNamePattern) _showSaveAttachments(messageId, part, attachmentNamePattern, j, jcount)
def _initSenderLabelsMap(sender): def _initSenderLabelsMap(sender):
if sender not in senderLabelsMaps: if sender not in senderLabelsMaps:
@@ -68047,8 +68156,8 @@ def printShowMessagesThreads(users, entityType):
Ind.Increment() Ind.Increment()
printKeyValueList([Ind.MultiLineText(_getMessageBody(result['payload']))]) printKeyValueList([Ind.MultiLineText(_getMessageBody(result['payload']))])
Ind.Decrement() Ind.Decrement()
if show_attachments or save_attachments: if show_attachments or save_attachments or upload_attachments:
_showSaveAttachments(result['id'], result['payload'], attachmentNamePattern) _showSaveAttachments(result['id'], result['payload'], attachmentNamePattern, j, jcount)
Ind.Decrement() Ind.Decrement()
parameters['messagesProcessed'] += 1 parameters['messagesProcessed'] += 1
@@ -68217,7 +68326,7 @@ def printShowMessagesThreads(users, entityType):
try: try:
response = callGAPI(service, 'get', response = callGAPI(service, 'get',
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_MESSAGE_ID], throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_MESSAGE_ID],
userId='me', id=ri[RI_ITEM], format=['metadata', 'full'][show_size or show_body or show_attachments or save_attachments]) userId='me', id=ri[RI_ITEM], format=['metadata', 'full'][show_size or show_body or show_attachments or save_attachments or upload_attachments])
if countsOnly: if countsOnly:
_callbacks['process'](ri[RI_ENTITY], response) _callbacks['process'](ri[RI_ENTITY], response)
else: else:
@@ -68254,7 +68363,7 @@ def printShowMessagesThreads(users, entityType):
_handleGmailError(exception, ri) _handleGmailError(exception, ri)
def _batchPrintShowMessagesThreads(service, user, jcount, messageIds): def _batchPrintShowMessagesThreads(service, user, jcount, messageIds):
svcargs = dict([('userId', 'me'), ('id', None), ('format', ['metadata', 'full'][show_body or show_attachments or save_attachments])]+GM.Globals[GM.EXTRA_ARGS_LIST]) svcargs = dict([('userId', 'me'), ('id', None), ('format', ['metadata', 'full'][show_body or show_attachments or save_attachments or upload_attachments])]+GM.Globals[GM.EXTRA_ARGS_LIST])
if countsOnly: if countsOnly:
if show_labels: if show_labels:
if not senderMatchPattern: if not senderMatchPattern:
@@ -68290,7 +68399,7 @@ def printShowMessagesThreads(users, entityType):
parameters = _initMessageThreadParameters(entityType, True, 0) parameters = _initMessageThreadParameters(entityType, True, 0)
convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL] convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL]
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
countsOnly = positiveCountsOnly = includeSpamTrash = onlyUser = overwrite = save_attachments = False countsOnly = positiveCountsOnly = includeSpamTrash = onlyUser = overwrite = save_attachments = upload_attachments = False
show_all_headers = show_attachments = show_body = show_date = show_labels = show_size = show_snippet = False show_all_headers = show_attachments = show_body = show_date = show_labels = show_size = show_snippet = False
noshow_text_plain = False noshow_text_plain = False
attachmentNamePattern = None attachmentNamePattern = None
@@ -68301,6 +68410,8 @@ def printShowMessagesThreads(users, entityType):
showMode = Act.Get() == Act.SHOW showMode = Act.Get() == Act.SHOW
dateHeaderFormat = '' dateHeaderFormat = ''
dateHeaderConvertTimezone = False dateHeaderConvertTimezone = False
uploadAttachmentBody = {}
parentParms = initDriveFileAttributes()
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if csvPF and myarg == 'todrive': if csvPF and myarg == 'todrive':
@@ -68336,6 +68447,10 @@ def printShowMessagesThreads(users, entityType):
targetFolderPattern = os.path.expanduser(getString(Cmd.OB_FILE_PATH)) targetFolderPattern = os.path.expanduser(getString(Cmd.OB_FILE_PATH))
elif showMode and myarg == 'overwrite': elif showMode and myarg == 'overwrite':
overwrite = getBoolean() overwrite = getBoolean()
elif showMode and myarg == 'uploadattachments':
upload_attachments = True
elif showMode and getDriveFileParentAttribute(myarg, parentParms):
pass
elif myarg == 'includespamtrash': elif myarg == 'includespamtrash':
includeSpamTrash = True includeSpamTrash = True
elif myarg == 'countsonly': elif myarg == 'countsonly':
@@ -68400,6 +68515,12 @@ def printShowMessagesThreads(users, entityType):
if not gmail: if not gmail:
continue continue
service = gmail.users().messages() if entityType == Ent.MESSAGE else gmail.users().threads() service = gmail.users().messages() if entityType == Ent.MESSAGE else gmail.users().threads()
if upload_attachments:
_, drive = buildGAPIServiceObject(API.DRIVE3, user, i, count)
if not drive:
continue
if not _getDriveFileParentInfo(drive, user, i, count, uploadAttachmentBody, parentParms):
continue
if show_labels or labelMatchPattern: if show_labels or labelMatchPattern:
labels = _getUserGmailLabels(gmail, user, i, count, 'labels(id,name,type)') labels = _getUserGmailLabels(gmail, user, i, count, 'labels(id,name,type)')
if not labels: if not labels:
@@ -68548,17 +68669,20 @@ def printShowMessagesThreads(users, entityType):
# [labelmatchpattern <RegularExpression>] [sendermatchpattern <RegularExpression>] # [labelmatchpattern <RegularExpression>] [sendermatchpattern <RegularExpression>]
# [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]] # [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
# [showlabels] [showbody] [showdate] [showsize] [showsnippet] # [showlabels] [showbody] [showdate] [showsize] [showsnippet]
# [showattachments [attachmentnamepattern <RegularExpression>]]
# [convertcrnl] [delimiter <Character>] [todrive <ToDriveAttribute>*] # [convertcrnl] [delimiter <Character>] [todrive <ToDriveAttribute>*]
# [countsonly|positivecountsonly] [useronly] # [countsonly|positivecountsonly] [useronly]
# [[attachmentnamepattern <RegularExpression>]
# [showattachments [noshowtextplain]]]
# gam <UserTypeEntity> show message|messages # gam <UserTypeEntity> show message|messages
# (((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])* [quick|notquick] [max_to_show <Number>] [includespamtrash])|(ids <MessageIDEntity>) # (((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])* [quick|notquick] [max_to_show <Number>] [includespamtrash])|(ids <MessageIDEntity>)
# [labelmatchpattern <RegularExpression>] [sendermatchpattern <RegularExpression>] # [labelmatchpattern <RegularExpression>] [sendermatchpattern <RegularExpression>]
# [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]] # [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
# [showlabels] [showbody] [showdate] [showsize] [showsnippet] # [showlabels] [showbody] [showdate] [showsize] [showsnippet]
# [showattachments [attachmentnamepattern <RegularExpression>] [noshowtextplain]]
# [countsonly|positivecountsonly] [useronly] # [countsonly|positivecountsonly] [useronly]
# [saveattachments [attachmentnamepattern <RegularExpression>]] [targetfolder <FilePath>] [overwrite [<Boolean>]] # [[attachmentnamepattern <RegularExpression>]
# [showattachments [noshowtextplain]]
# [saveattachments [targetfolder <FilePath>] [overwrite [<Boolean>]]]
# [uploadattachments [<DriveFileParentAttribute>]]]
def printShowMessages(users): def printShowMessages(users):
printShowMessagesThreads(users, Ent.MESSAGE) printShowMessagesThreads(users, Ent.MESSAGE)
@@ -68567,17 +68691,20 @@ def printShowMessages(users):
# [labelmatchpattern <RegularExpression>] # [labelmatchpattern <RegularExpression>]
# [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]] # [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
# [showlabels] [showbody] [showdate] [showsize] [showsnippet] # [showlabels] [showbody] [showdate] [showsize] [showsnippet]
# [showattachments [attachmentnamepattern <RegularExpression>]]
# [convertcrnl] [delimiter <Character>] [todrive <ToDriveAttribute>*] # [convertcrnl] [delimiter <Character>] [todrive <ToDriveAttribute>*]
# [countsonly|positivecountsonly] [useronly] # [countsonly|positivecountsonly] [useronly]
# [[attachmentnamepattern <RegularExpression>]
# [showattachments [noshowtextplain]]]
# gam <UserTypeEntity> show thread|threads # gam <UserTypeEntity> show thread|threads
# (((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])* [quick|notquick] [max_to_show <Number>] [includespamtrash])|(ids <ThreadIDEntity>) # (((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])* [quick|notquick] [max_to_show <Number>] [includespamtrash])|(ids <ThreadIDEntity>)
# [labelmatchpattern <RegularExpression>] # [labelmatchpattern <RegularExpression>]
# [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]] # [headers all|<SMTPHeaderList>] [dateheaderformat iso|rfc2822|<String>] [dateheaderconverttimezone [<Boolean>]]
# [showlabels] [showbody] [showdate] [showsize] [showsnippet] # [showlabels] [showbody] [showdate] [showsize] [showsnippet]
# [showattachments [attachmentnamepattern <RegularExpression>] [noshowtextplain]]
# [countsonly|positivecountsonly] [useronly] # [countsonly|positivecountsonly] [useronly]
# [saveattachments [attachmentnamepattern <RegularExpression>]] [targetfolder <FilePath>] [overwrite [<Boolean>]] # [[attachmentnamepattern <RegularExpression>]
# [showattachments [noshowtextplain]]
# [saveattachments [targetfolder <FilePath>] [overwrite [<Boolean>]]]
# [uploadattachments [<DriveFileParentAttribute>]]]
def printShowThreads(users): def printShowThreads(users):
printShowMessagesThreads(users, Ent.THREAD) printShowMessagesThreads(users, Ent.THREAD)
@@ -73048,6 +73175,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
Cmd.ARG_TRANSFERAPPS: doShowTransferApps, Cmd.ARG_TRANSFERAPPS: doShowTransferApps,
Cmd.ARG_USER: doPrintUsers, Cmd.ARG_USER: doPrintUsers,
Cmd.ARG_USERS: doPrintUsers, Cmd.ARG_USERS: doPrintUsers,
Cmd.ARG_USERCOUNTSBYORGUNIT: doPrintUserCountsByOrgUnit,
Cmd.ARG_USERINVITATION: doPrintShowCIUserInvitations, Cmd.ARG_USERINVITATION: doPrintShowCIUserInvitations,
Cmd.ARG_VAULTCOUNT: doPrintVaultCounts, Cmd.ARG_VAULTCOUNT: doPrintVaultCounts,
Cmd.ARG_VAULTEXPORT: doPrintShowVaultExports, Cmd.ARG_VAULTEXPORT: doPrintShowVaultExports,

View File

@@ -72,6 +72,7 @@ KEEP = 'keep'
LICENSING = 'licensing' LICENSING = 'licensing'
LOOKERSTUDIO = 'datastudio' LOOKERSTUDIO = 'datastudio'
OAUTH2 = 'oauth2' OAUTH2 = 'oauth2'
ORGPOLICY = 'orgpolicy'
PEOPLE = 'people' PEOPLE = 'people'
PEOPLE_DIRECTORY = 'peopledirectory' PEOPLE_DIRECTORY = 'peopledirectory'
PEOPLE_OTHERCONTACTS = 'peopleothercontacts' PEOPLE_OTHERCONTACTS = 'peopleothercontacts'
@@ -112,7 +113,7 @@ REQUIRED_SCOPES_SET = set(REQUIRED_SCOPES)
JWT_APIS = { JWT_APIS = {
ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE], ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE],
CHAT: ['https://www.googleapis.com/auth/chat.bot'], CHAT: ['https://www.googleapis.com/auth/chat.bot'],
CLOUDRESOURCEMANAGER: [CLOUD_PLATFORM_SCOPE] ORGPOLICY: [CLOUD_PLATFORM_SCOPE],
} }
# #
APIS_NEEDING_ACCESS_TOKEN = { APIS_NEEDING_ACCESS_TOKEN = {
@@ -236,6 +237,7 @@ _INFO = {
LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True}, LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True},
LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True}, LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True},
OAUTH2: {'name': 'OAuth2 API', 'version': 'v2', 'v2discovery': False}, OAUTH2: {'name': 'OAuth2 API', 'version': 'v2', 'v2discovery': False},
ORGPOLICY: {'name': 'Organization Policy API', 'version': 'v2', 'v2discovery': True},
PEOPLE: {'name': 'People API', 'version': 'v1', 'v2discovery': True}, PEOPLE: {'name': 'People API', 'version': 'v1', 'v2discovery': True},
PEOPLE_DIRECTORY: {'name': 'People Directory API', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE}, PEOPLE_DIRECTORY: {'name': 'People Directory API', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE},
PEOPLE_OTHERCONTACTS: {'name': 'People API - Other Contacts', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE}, PEOPLE_OTHERCONTACTS: {'name': 'People API - Other Contacts', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE},

View File

@@ -776,6 +776,7 @@ class GamCLArgs():
ARG_TRUSTEDAPPS = 'trustedapps' ARG_TRUSTEDAPPS = 'trustedapps'
ARG_USER = 'user' ARG_USER = 'user'
ARG_USERS = 'users' ARG_USERS = 'users'
ARG_USERCOUNTSBYORGUNIT = 'usercountsbyorgunit'
ARG_USERINVITATION = 'userinvitation' ARG_USERINVITATION = 'userinvitation'
ARG_USERINVITATIONS = 'userinvitations' ARG_USERINVITATIONS = 'userinvitations'
ARG_USERLIST = 'userlist' ARG_USERLIST = 'userlist'