mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-05 14:51:39 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73677544a3 | ||
|
|
7c46d8548e | ||
|
|
186381426a | ||
|
|
af1e695661 | ||
|
|
4ccd51269a | ||
|
|
560cfe225f | ||
|
|
e9e4c3d333 | ||
|
|
dbca6e3b88 | ||
|
|
ad465ed20c | ||
|
|
9370f7ce15 | ||
|
|
d9151a866b | ||
|
|
7937fd00d4 | ||
|
|
d2199a5b9c | ||
|
|
6e765325c1 | ||
|
|
18119b3d64 | ||
|
|
378a7c2d6c | ||
|
|
1270a315b2 | ||
|
|
931b2cc700 | ||
|
|
e145ac0ad1 | ||
|
|
ab8e882e94 | ||
|
|
b66d671b74 | ||
|
|
f662a13778 | ||
|
|
845aa122e1 | ||
|
|
bb19336d06 | ||
|
|
774948cf9d | ||
|
|
e26e077c83 | ||
|
|
f264ffd040 | ||
|
|
7e16e4880b | ||
|
|
dd1ee6ff44 | ||
|
|
90d628cc75 | ||
|
|
d5a0b33f04 |
15
.travis.yml
15
.travis.yml
@@ -39,7 +39,14 @@ cache:
|
||||
- $HOME/ssl
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- python: nightly
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
name: "Linux 64-bit Focal"
|
||||
dist: focal
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux 64-bit Bionic"
|
||||
dist: bionic
|
||||
@@ -69,7 +76,11 @@ jobs:
|
||||
language: python
|
||||
python: 3.7
|
||||
- os: linux
|
||||
name: "Python nightly Source Testing"
|
||||
name: "Python 3.9 dev Source Testing"
|
||||
language: python
|
||||
python: 3.9-dev
|
||||
- os: linux
|
||||
name: "Python trunk nightly Source Testing"
|
||||
language: python
|
||||
python: nightly
|
||||
- os: linux
|
||||
@@ -87,7 +98,7 @@ jobs:
|
||||
- os: osx
|
||||
name: "MacOS 10.15"
|
||||
language: generic
|
||||
osx_image: xcode11.4
|
||||
osx_image: xcode11.5
|
||||
- os: windows
|
||||
name: "Windows 64-bit"
|
||||
language: shell
|
||||
|
||||
@@ -780,7 +780,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<UserBasicAttribute> ::=
|
||||
(agreed2terms|agreedtoterms <Boolean>)|
|
||||
(changepassword|changepasswordatnextlogin <Boolean>)|
|
||||
(crypt|sha|sha1|sha-1|md5|nohash)|
|
||||
(base64-md5|base64-sha1|crypt|sha|sha1|sha-1|md5|nohash)|
|
||||
(customerid <String>)|
|
||||
(email|primaryemail|username <EmailAddress>)|
|
||||
(firstname|givenname <String>)|
|
||||
@@ -822,6 +822,7 @@ gam help
|
||||
|
||||
gam batch <FileName>|- [charset <Charset>]
|
||||
gam csv <FileName>|- [charset <Charset>] gam <GAM argument list>
|
||||
gam csvtest <FileName>|- [charset <Charset>] gam <GAM argument list>
|
||||
|
||||
You can make substitutions in <GAMArgumentList> with values from the CSV file.
|
||||
An argument containing exactly ~xxx is replaced by the value of field xxx from the CSV file
|
||||
|
||||
@@ -1,486 +0,0 @@
|
||||
{
|
||||
"kind": "discovery#restDescription",
|
||||
"discoveryVersion": "v1",
|
||||
"id": "cloudprint:v2",
|
||||
"name": "cloudprint",
|
||||
"version": "v2",
|
||||
"revision": "20150605",
|
||||
"title": "Cloud Print API",
|
||||
"description": "Lets you access Cloud Print Printers",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"icons": {
|
||||
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||
},
|
||||
"documentationLink": "https://developers.google.com/cloud-print",
|
||||
"protocol": "rest",
|
||||
"baseUrl": "https://www.google.com/",
|
||||
"basePath": "/cloudprint/",
|
||||
"rootUrl": "https://www.google.com/",
|
||||
"servicePath": "/cloudprint/",
|
||||
"parameters": {
|
||||
"prettyPrint": {
|
||||
"type": "boolean",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"default": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/cloudprint": {
|
||||
"description": "Manage Cloud Print"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {
|
||||
"Job": {
|
||||
"id": "Job",
|
||||
"type": "object",
|
||||
"description": "Job Object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Job Title"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Jobs": {
|
||||
"id": "Jobs",
|
||||
"type": "object",
|
||||
"description": "List of Jobs.",
|
||||
"properties": {
|
||||
"jobs": {
|
||||
"type": "array",
|
||||
"description": "List of job objects.",
|
||||
"items": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Printer": {
|
||||
"id": "Printer",
|
||||
"type": "object",
|
||||
"description": "Printer Object",
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"description": "Display Name"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Unique ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Printers": {
|
||||
"id": "Printers",
|
||||
"type": "object",
|
||||
"description": "List of Printers.",
|
||||
"properties": {
|
||||
"printers": {
|
||||
"type": "array",
|
||||
"description": "List of printer objects.",
|
||||
"items": {
|
||||
"$ref": "Printer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resources": {
|
||||
"jobs": {
|
||||
"methods": {
|
||||
"delete": {
|
||||
"id": "cloudprint.jobs.delete",
|
||||
"path": "deletejob",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"location": "query",
|
||||
"required": "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fetch": {
|
||||
"id": "cloudprint.jobs.fetch",
|
||||
"path": "fetch",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"getticket": {
|
||||
"id": "cloudprint.jobs.getticket",
|
||||
"path": "ticket",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"use_cjt": {
|
||||
"type": "boolean",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"id": "cloudprint.jobs.list",
|
||||
"path": "jobs",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"owner": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"q": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"offset": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"limit": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"sortorder": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"id": "cloudprint.jobs.update",
|
||||
"path": "control",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"semantic_state_diff": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Jobs"
|
||||
}
|
||||
},
|
||||
"resubmit": {
|
||||
"id": "cloudprint.jobs.resubmit",
|
||||
"path": "resubmit",
|
||||
"httpMethod": "POST",
|
||||
"description": "resubmit a job to new printer.",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"jobid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"ticket": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"id": "cloudprint.jobs.submit",
|
||||
"path": "submit",
|
||||
"httpMethod": "POST",
|
||||
"description": "Send a print job to cloud print.",
|
||||
"request": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"ticket": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"contentType": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Job"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"printers": {
|
||||
"methods": {
|
||||
"get": {
|
||||
"id": "cloudprint.printers.get",
|
||||
"path": "printer",
|
||||
"httpMethod": "GET",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"extra_fields": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Printer"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
"id": "cloudprint.printers.list",
|
||||
"path": "search",
|
||||
"httpMethod": "GET",
|
||||
"description": "List all printers",
|
||||
"parameters": {
|
||||
"q": {
|
||||
"type": "string",
|
||||
"description": "Query list of printers",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "limit results to printers of type",
|
||||
"location": "query"
|
||||
},
|
||||
"connection_status": {
|
||||
"type": "string",
|
||||
"description": "limit results to printers with this status",
|
||||
"location": "query"
|
||||
},
|
||||
"extra_fields": {
|
||||
"type": "string",
|
||||
"description": "include extra fields",
|
||||
"location": "query"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"$ref": "Printers"
|
||||
}
|
||||
},
|
||||
"share": {
|
||||
"id": "cloudprint.printers.share",
|
||||
"path": "share",
|
||||
"httpMethod": "GET",
|
||||
"description": "Share printer with user, group or domain",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"skip_notification": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"unshare": {
|
||||
"id": "cloudprint.printers.unshare",
|
||||
"path": "unshare",
|
||||
"httpMethod": "GET",
|
||||
"description": "unshare printer with user, group or domain",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"id": "cloudprint.printers.delete",
|
||||
"path": "delete",
|
||||
"httpMethod": "GET",
|
||||
"description": "delete a printer",
|
||||
"parameters": {
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"id": "cloudprint.printers.update",
|
||||
"path": "update",
|
||||
"httpMethod": "GET",
|
||||
"description": "update a printer",
|
||||
"parameters": {
|
||||
"isTosAccepted": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"gcpVersion": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"setupUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"supportUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"firmware": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"currentQuota": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"public": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"proxy": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"manufacturer": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"defaultDisplayName": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"uuid": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"updateUrl": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"ownerId": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
},
|
||||
"printerid": {
|
||||
"type": "string",
|
||||
"required": "true",
|
||||
"location": "query"
|
||||
},
|
||||
"quotaEnabled": {
|
||||
"type": "boolean",
|
||||
"location": "query"
|
||||
},
|
||||
"dailyQuota": {
|
||||
"type": "string",
|
||||
"location": "query"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ gamversion="latest"
|
||||
adminuser=""
|
||||
regularuser=""
|
||||
gam_glibc_vers="2.27 2.23"
|
||||
gam_macos_vers="10.14.6 10.13.6"
|
||||
gam_macos_vers="10.15.4 10.14.6 10.13.6"
|
||||
|
||||
while getopts "hd:a:o:b:lp:u:r:v:" OPTION
|
||||
do
|
||||
|
||||
@@ -7,11 +7,9 @@ from PyInstaller.utils.hooks import copy_metadata
|
||||
|
||||
sys.modules['FixTk'] = None
|
||||
|
||||
extra_files = [('cloudprint-v2.json', 'cloudprint-v2.json')]
|
||||
|
||||
# dynamically determine where httplib2/cacerts.txt lives
|
||||
proot = os.path.dirname(importlib.import_module('httplib2').__file__)
|
||||
extra_files += [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
extra_files = [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
|
||||
extra_files += copy_metadata('google-api-python-client')
|
||||
|
||||
|
||||
@@ -2783,695 +2783,6 @@ def doPrintCourseParticipants():
|
||||
display.write_csv_file(csvRows, titles, 'Course Participants', todrive)
|
||||
|
||||
|
||||
def doPrintPrintJobs():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
todrive = False
|
||||
titles = ['printerid', 'id']
|
||||
csvRows = []
|
||||
printerid = None
|
||||
owner = None
|
||||
status = None
|
||||
sortorder = None
|
||||
descending = False
|
||||
query = None
|
||||
age = None
|
||||
older_or_newer = None
|
||||
jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg in ['olderthan', 'newerthan']:
|
||||
if myarg == 'olderthan':
|
||||
older_or_newer = 'older'
|
||||
else:
|
||||
older_or_newer = 'newer'
|
||||
age_number = sys.argv[i + 1][:-1]
|
||||
if not age_number.isdigit():
|
||||
controlflow.system_error_exit(
|
||||
2, f'expected a number; got {age_number}')
|
||||
age_unit = sys.argv[i + 1][-1].lower()
|
||||
if age_unit == 'm':
|
||||
age = int(time.time()) - (int(age_number) * 60)
|
||||
elif age_unit == 'h':
|
||||
age = int(time.time()) - (int(age_number) * 60 * 60)
|
||||
elif age_unit == 'd':
|
||||
age = int(time.time()) - (int(age_number) * 60 * 60 * 24)
|
||||
else:
|
||||
controlflow.system_error_exit(
|
||||
2,
|
||||
f'expected m (minutes), h (hours) or d (days); got {age_unit}'
|
||||
)
|
||||
i += 2
|
||||
elif myarg == 'query':
|
||||
query = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'status':
|
||||
status = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'ascending':
|
||||
descending = False
|
||||
i += 1
|
||||
elif myarg == 'descending':
|
||||
descending = True
|
||||
i += 1
|
||||
elif myarg == 'orderby':
|
||||
sortorder = sys.argv[i + 1].lower().replace('_', '')
|
||||
if sortorder not in PRINTJOB_ASCENDINGORDER_MAP:
|
||||
controlflow.expected_argument_exit(
|
||||
'orderby', ', '.join(PRINTJOB_ASCENDINGORDER_MAP),
|
||||
sortorder)
|
||||
sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder]
|
||||
i += 2
|
||||
elif myarg in ['printer', 'printerid']:
|
||||
printerid = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['owner', 'user']:
|
||||
owner = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'limit':
|
||||
jobLimit = getInteger(sys.argv[i + 1], myarg, minVal=0)
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print printjobs')
|
||||
if sortorder and descending:
|
||||
sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder]
|
||||
if printerid:
|
||||
result = gapi.call(cp.printers(), 'get', printerid=printerid)
|
||||
checkCloudPrintResult(result)
|
||||
if ((not sortorder) or
|
||||
(sortorder == 'CREATE_TIME_DESC')) and (older_or_newer == 'newer'):
|
||||
timeExit = True
|
||||
elif (sortorder == 'CREATE_TIME') and (older_or_newer == 'older'):
|
||||
timeExit = True
|
||||
else:
|
||||
timeExit = False
|
||||
jobCount = offset = 0
|
||||
while True:
|
||||
if jobLimit == 0:
|
||||
limit = PRINTJOBS_DEFAULT_MAX_RESULTS
|
||||
else:
|
||||
limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit - jobCount)
|
||||
if limit == 0:
|
||||
break
|
||||
result = gapi.call(cp.jobs(),
|
||||
'list',
|
||||
printerid=printerid,
|
||||
q=query,
|
||||
status=status,
|
||||
sortorder=sortorder,
|
||||
owner=owner,
|
||||
offset=offset,
|
||||
limit=limit)
|
||||
checkCloudPrintResult(result)
|
||||
newJobs = result['range']['jobsCount']
|
||||
totalJobs = int(result['range']['jobsTotal'])
|
||||
if GC_Values[GC_DEBUG_LEVEL] > 0:
|
||||
sys.stderr.write(
|
||||
f'Debug: jobCount: {jobCount}, jobLimit: {jobLimit}, jobsCount: {newJobs}, jobsTotal: {totalJobs}\n'
|
||||
)
|
||||
if newJobs == 0:
|
||||
break
|
||||
jobCount += newJobs
|
||||
offset += newJobs
|
||||
for job in result['jobs']:
|
||||
createTime = int(job['createTime']) / 1000
|
||||
if older_or_newer:
|
||||
if older_or_newer == 'older' and createTime > age:
|
||||
if timeExit:
|
||||
jobCount = totalJobs
|
||||
break
|
||||
continue
|
||||
if older_or_newer == 'newer' and createTime < age:
|
||||
if timeExit:
|
||||
jobCount = totalJobs
|
||||
break
|
||||
continue
|
||||
job['createTime'] = utils.formatTimestampYMDHMS(job['createTime'])
|
||||
job['updateTime'] = utils.formatTimestampYMDHMS(job['updateTime'])
|
||||
job['tags'] = ' '.join(job['tags'])
|
||||
display.add_row_titles_to_csv_file(utils.flatten_json(job), csvRows,
|
||||
titles)
|
||||
if jobCount >= totalJobs:
|
||||
break
|
||||
display.write_csv_file(csvRows, titles, 'Print Jobs', todrive)
|
||||
|
||||
|
||||
def doPrintPrinters():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
todrive = False
|
||||
titles = [
|
||||
'id',
|
||||
]
|
||||
csvRows = []
|
||||
queries = [None]
|
||||
printer_type = None
|
||||
connection_status = None
|
||||
extra_fields = None
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['query', 'queries']:
|
||||
queries = getQueries(myarg, sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'type':
|
||||
printer_type = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'status':
|
||||
connection_status = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'extrafields':
|
||||
extra_fields = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print printers')
|
||||
for query in queries:
|
||||
printers = gapi.call(cp.printers(),
|
||||
'list',
|
||||
q=query,
|
||||
type=printer_type,
|
||||
connection_status=connection_status,
|
||||
extra_fields=extra_fields)
|
||||
checkCloudPrintResult(printers)
|
||||
for printer in printers['printers']:
|
||||
printer['createTime'] = utils.formatTimestampYMDHMS(
|
||||
printer['createTime'])
|
||||
printer['accessTime'] = utils.formatTimestampYMDHMS(
|
||||
printer['accessTime'])
|
||||
printer['updateTime'] = utils.formatTimestampYMDHMS(
|
||||
printer['updateTime'])
|
||||
printer['tags'] = ' '.join(printer['tags'])
|
||||
display.add_row_titles_to_csv_file(utils.flatten_json(printer),
|
||||
csvRows, titles)
|
||||
display.write_csv_file(csvRows, titles, 'Printers', todrive)
|
||||
|
||||
|
||||
def doPrinterShowACL():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
show_printer = sys.argv[2]
|
||||
printer_info = gapi.call(cp.printers(), 'get', printerid=show_printer)
|
||||
checkCloudPrintResult(printer_info)
|
||||
for acl in printer_info['printers'][0]['access']:
|
||||
if 'key' in acl:
|
||||
acl['accessURL'] = f'https://www.google.com/cloudprint/addpublicprinter.html?printerid={show_printer}&key={acl["key"]}'
|
||||
display.print_json(acl)
|
||||
print()
|
||||
|
||||
|
||||
def doPrinterAddACL():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printer = sys.argv[2]
|
||||
role = sys.argv[4].upper()
|
||||
scope = sys.argv[5]
|
||||
notify = bool(len(sys.argv) > 6 and sys.argv[6].lower() == 'notify')
|
||||
public = None
|
||||
skip_notification = True
|
||||
if scope.lower() == 'public':
|
||||
public = True
|
||||
scope = None
|
||||
role = None
|
||||
skip_notification = None
|
||||
elif scope.find('@') == -1:
|
||||
scope = f'/hd/domain/{scope}'
|
||||
else:
|
||||
skip_notification = not notify
|
||||
result = gapi.call(cp.printers(),
|
||||
'share',
|
||||
printerid=printer,
|
||||
role=role,
|
||||
scope=scope,
|
||||
public=public,
|
||||
skip_notification=skip_notification)
|
||||
checkCloudPrintResult(result)
|
||||
who = scope
|
||||
if who is None:
|
||||
who = 'public'
|
||||
role = 'user'
|
||||
print(f'Added {role} {who}')
|
||||
|
||||
|
||||
def doPrinterDelACL():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printer = sys.argv[2]
|
||||
scope = sys.argv[4]
|
||||
public = None
|
||||
if scope.lower() == 'public':
|
||||
public = True
|
||||
scope = None
|
||||
elif scope.find('@') == -1:
|
||||
scope = f'/hd/domain/{scope}'
|
||||
result = gapi.call(cp.printers(),
|
||||
'unshare',
|
||||
printerid=printer,
|
||||
scope=scope,
|
||||
public=public)
|
||||
checkCloudPrintResult(result)
|
||||
who = scope
|
||||
if who is None:
|
||||
who = 'public'
|
||||
print(f'Removed {who}')
|
||||
|
||||
|
||||
def encode_multipart(fields, files, boundary=None):
|
||||
|
||||
def escape_quote(s):
|
||||
return s.replace('"', '\\"')
|
||||
|
||||
def getFormDataLine(name, value, boundary):
|
||||
return f'--{boundary}', f'Content-Disposition: form-data; name="{escape_quote(name)}"', '', str(
|
||||
value)
|
||||
|
||||
if boundary is None:
|
||||
boundary = ''.join(random.choice(ALPHANUMERIC_CHARS) for _ in range(30))
|
||||
lines = []
|
||||
for name, value in list(fields.items()):
|
||||
if name == 'tags':
|
||||
for tag in value:
|
||||
lines.extend(getFormDataLine('tag', tag, boundary))
|
||||
else:
|
||||
lines.extend(getFormDataLine(name, value, boundary))
|
||||
for name, value in list(files.items()):
|
||||
filename = value['filename']
|
||||
mimetype = value['mimetype']
|
||||
lines.extend((
|
||||
f'--{boundary}',
|
||||
f'Content-Disposition: form-data; name="{escape_quote(name)}"; filename="{escape_quote(filename)}"',
|
||||
f'Content-Type: {mimetype}',
|
||||
'',
|
||||
value['content'],
|
||||
))
|
||||
lines.extend((
|
||||
f'--{boundary}--',
|
||||
'',
|
||||
))
|
||||
body = '\r\n'.join(lines)
|
||||
headers = {
|
||||
'Content-Type': f'multipart/form-data; boundary={boundary}',
|
||||
'Content-Length': str(len(body)),
|
||||
}
|
||||
return (body, headers)
|
||||
|
||||
|
||||
def doPrintJobFetch():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printerid = sys.argv[2]
|
||||
if printerid == 'any':
|
||||
printerid = None
|
||||
owner = None
|
||||
status = None
|
||||
sortorder = None
|
||||
descending = False
|
||||
query = None
|
||||
age = None
|
||||
older_or_newer = None
|
||||
jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT
|
||||
targetFolder = os.getcwd()
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['olderthan', 'newerthan']:
|
||||
if myarg == 'olderthan':
|
||||
older_or_newer = 'older'
|
||||
else:
|
||||
older_or_newer = 'newer'
|
||||
age_number = sys.argv[i + 1][:-1]
|
||||
if not age_number.isdigit():
|
||||
controlflow.system_error_exit(
|
||||
2, f'expected a number; got {age_number}')
|
||||
age_unit = sys.argv[i + 1][-1].lower()
|
||||
if age_unit == 'm':
|
||||
age = int(time.time()) - (int(age_number) * 60)
|
||||
elif age_unit == 'h':
|
||||
age = int(time.time()) - (int(age_number) * 60 * 60)
|
||||
elif age_unit == 'd':
|
||||
age = int(time.time()) - (int(age_number) * 60 * 60 * 24)
|
||||
else:
|
||||
controlflow.system_error_exit(
|
||||
2,
|
||||
f'expected m (minutes), h (hours) or d (days); got {age_unit}'
|
||||
)
|
||||
i += 2
|
||||
elif myarg == 'query':
|
||||
query = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'status':
|
||||
status = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'ascending':
|
||||
descending = False
|
||||
i += 1
|
||||
elif myarg == 'descending':
|
||||
descending = True
|
||||
i += 1
|
||||
elif myarg == 'orderby':
|
||||
sortorder = sys.argv[i + 1].lower().replace('_', '')
|
||||
if sortorder not in PRINTJOB_ASCENDINGORDER_MAP:
|
||||
controlflow.expected_argument_exit(
|
||||
'orderby', ', '.join(PRINTJOB_ASCENDINGORDER_MAP),
|
||||
sortorder)
|
||||
sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder]
|
||||
i += 2
|
||||
elif myarg in ['owner', 'user']:
|
||||
owner = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'limit':
|
||||
jobLimit = getInteger(sys.argv[i + 1], myarg, minVal=0)
|
||||
i += 2
|
||||
elif myarg == 'drivedir':
|
||||
targetFolder = GC_Values[GC_DRIVE_DIR]
|
||||
i += 1
|
||||
elif myarg == 'targetfolder':
|
||||
targetFolder = os.path.expanduser(sys.argv[i + 1])
|
||||
if not os.path.isdir(targetFolder):
|
||||
os.makedirs(targetFolder)
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam printjobs fetch')
|
||||
if sortorder and descending:
|
||||
sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder]
|
||||
if printerid:
|
||||
result = gapi.call(cp.printers(), 'get', printerid=printerid)
|
||||
checkCloudPrintResult(result)
|
||||
ssd = '{"state": {"type": "DONE"}}'
|
||||
if ((not sortorder) or
|
||||
(sortorder == 'CREATE_TIME_DESC')) and (older_or_newer == 'newer'):
|
||||
timeExit = True
|
||||
elif (sortorder == 'CREATE_TIME') and (older_or_newer == 'older'):
|
||||
timeExit = True
|
||||
else:
|
||||
timeExit = False
|
||||
jobCount = offset = 0
|
||||
while True:
|
||||
if jobLimit == 0:
|
||||
limit = PRINTJOBS_DEFAULT_MAX_RESULTS
|
||||
else:
|
||||
limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit - jobCount)
|
||||
if limit == 0:
|
||||
break
|
||||
result = gapi.call(cp.jobs(),
|
||||
'list',
|
||||
printerid=printerid,
|
||||
q=query,
|
||||
status=status,
|
||||
sortorder=sortorder,
|
||||
owner=owner,
|
||||
offset=offset,
|
||||
limit=limit)
|
||||
checkCloudPrintResult(result)
|
||||
newJobs = result['range']['jobsCount']
|
||||
totalJobs = int(result['range']['jobsTotal'])
|
||||
if newJobs == 0:
|
||||
break
|
||||
jobCount += newJobs
|
||||
offset += newJobs
|
||||
for job in result['jobs']:
|
||||
createTime = int(job['createTime']) / 1000
|
||||
if older_or_newer:
|
||||
if older_or_newer == 'older' and createTime > age:
|
||||
if timeExit:
|
||||
jobCount = totalJobs
|
||||
break
|
||||
continue
|
||||
if older_or_newer == 'newer' and createTime < age:
|
||||
if timeExit:
|
||||
jobCount = totalJobs
|
||||
break
|
||||
continue
|
||||
fileUrl = job['fileUrl']
|
||||
jobid = job['id']
|
||||
fileName = os.path.join(
|
||||
targetFolder,
|
||||
f'{"".join(c if c in FILENAME_SAFE_CHARS else "_" for c in job["title"])}-{jobid}'
|
||||
)
|
||||
_, content = cp._http.request(uri=fileUrl, method='GET')
|
||||
if fileutils.write_file(fileName,
|
||||
content,
|
||||
mode='wb',
|
||||
continue_on_error=True):
|
||||
# ticket = gapi.call(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True)
|
||||
result = gapi.call(cp.jobs(),
|
||||
'update',
|
||||
jobid=jobid,
|
||||
semantic_state_diff=ssd)
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Printed job {jobid} to {fileName}')
|
||||
if jobCount >= totalJobs:
|
||||
break
|
||||
if jobCount == 0:
|
||||
print('No print jobs.')
|
||||
|
||||
|
||||
def doDelPrinter():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printerid = sys.argv[3]
|
||||
result = gapi.call(cp.printers(), 'delete', printerid=printerid)
|
||||
checkCloudPrintResult(result)
|
||||
|
||||
|
||||
def doGetPrinterInfo():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printerid = sys.argv[3]
|
||||
everything = False
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'everything':
|
||||
everything = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam info printer')
|
||||
result = gapi.call(cp.printers(), 'get', printerid=printerid)
|
||||
checkCloudPrintResult(result)
|
||||
printer_info = result['printers'][0]
|
||||
printer_info['createTime'] = utils.formatTimestampYMDHMS(
|
||||
printer_info['createTime'])
|
||||
printer_info['accessTime'] = utils.formatTimestampYMDHMS(
|
||||
printer_info['accessTime'])
|
||||
printer_info['updateTime'] = utils.formatTimestampYMDHMS(
|
||||
printer_info['updateTime'])
|
||||
printer_info['tags'] = ' '.join(printer_info['tags'])
|
||||
if not everything:
|
||||
del printer_info['capabilities']
|
||||
del printer_info['access']
|
||||
display.print_json(printer_info)
|
||||
|
||||
|
||||
def doUpdatePrinter():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printerid = sys.argv[3]
|
||||
kwargs = {}
|
||||
i = 4
|
||||
update_items = [
|
||||
'isTosAccepted', 'gcpVersion', 'setupUrl', 'quotaEnabled', 'id',
|
||||
'supportUrl', 'firmware', 'currentQuota', 'type', 'public', 'status',
|
||||
'description', 'defaultDisplayName', 'proxy', 'dailyQuota',
|
||||
'manufacturer', 'displayName', 'name', 'uuid', 'updateUrl', 'ownerId',
|
||||
'model'
|
||||
]
|
||||
while i < len(sys.argv):
|
||||
arg_in_item = False
|
||||
for item in update_items:
|
||||
if item.lower() == sys.argv[i].lower():
|
||||
kwargs[item] = sys.argv[i + 1]
|
||||
i += 2
|
||||
arg_in_item = True
|
||||
break
|
||||
if not arg_in_item:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam update printer')
|
||||
result = gapi.call(cp.printers(), 'update', printerid=printerid, **kwargs)
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Updated printer {printerid}')
|
||||
|
||||
|
||||
def doPrinterRegister():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
form_fields = {
|
||||
'name':
|
||||
'GAM',
|
||||
'proxy':
|
||||
'GAM',
|
||||
'uuid':
|
||||
_getValueFromOAuth('sub'),
|
||||
'manufacturer':
|
||||
GAM_AUTHOR,
|
||||
'model':
|
||||
'cp1',
|
||||
'gcp_version':
|
||||
'2.0',
|
||||
'setup_url':
|
||||
GAM_URL,
|
||||
'support_url':
|
||||
'https://groups.google.com/forum/#!forum/google-apps-manager',
|
||||
'update_url':
|
||||
GAM_RELEASES,
|
||||
'firmware':
|
||||
GAM_VERSION,
|
||||
'semantic_state': {
|
||||
'version': '1.0',
|
||||
'printer': {
|
||||
'state': 'IDLE',
|
||||
}
|
||||
},
|
||||
'use_cdd':
|
||||
True,
|
||||
'capabilities': {
|
||||
'version': '1.0',
|
||||
'printer': {
|
||||
'supported_content_type': [{
|
||||
'content_type': 'application/pdf',
|
||||
'min_version': '1.5'
|
||||
}, {
|
||||
'content_type': 'image/jpeg'
|
||||
}, {
|
||||
'content_type': 'text/plain'
|
||||
}],
|
||||
'copies': {
|
||||
'default': 1,
|
||||
'max': 100
|
||||
},
|
||||
'media_size': {
|
||||
'option': [{
|
||||
'name': 'ISO_A4',
|
||||
'width_microns': 210000,
|
||||
'height_microns': 297000
|
||||
}, {
|
||||
'name': 'NA_LEGAL',
|
||||
'width_microns': 215900,
|
||||
'height_microns': 355600
|
||||
}, {
|
||||
'name': 'NA_LETTER',
|
||||
'width_microns': 215900,
|
||||
'height_microns': 279400,
|
||||
'is_default': True
|
||||
}],
|
||||
},
|
||||
},
|
||||
},
|
||||
'tags': ['GAM', GAM_URL],
|
||||
}
|
||||
body, headers = encode_multipart(form_fields, {})
|
||||
#Get the printer first to make sure our OAuth access token is fresh
|
||||
gapi.call(cp.printers(), 'list')
|
||||
_, result = cp._http.request(
|
||||
uri='https://www.google.com/cloudprint/register',
|
||||
method='POST',
|
||||
body=body,
|
||||
headers=headers)
|
||||
result = json.loads(result.decode(UTF8))
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Created printer {result["printers"][0]["id"]}')
|
||||
|
||||
|
||||
def doPrintJobResubmit():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
jobid = sys.argv[2]
|
||||
printerid = sys.argv[4]
|
||||
ssd = '{"state": {"type": "HELD"}}'
|
||||
result = gapi.call(cp.jobs(),
|
||||
'update',
|
||||
jobid=jobid,
|
||||
semantic_state_diff=ssd)
|
||||
checkCloudPrintResult(result)
|
||||
ticket = gapi.call(cp.jobs(), 'getticket', jobid=jobid, use_cjt=True)
|
||||
result = gapi.call(cp.jobs(),
|
||||
'resubmit',
|
||||
printerid=printerid,
|
||||
jobid=jobid,
|
||||
ticket=ticket)
|
||||
checkCloudPrintResult(result)
|
||||
print(
|
||||
f'Success resubmitting {jobid} as job {result["job"]["id"]} to printer {printerid}'
|
||||
)
|
||||
|
||||
|
||||
def doPrintJobSubmit():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
printer = sys.argv[2]
|
||||
content = sys.argv[4]
|
||||
form_fields = {
|
||||
'printerid': printer,
|
||||
'title': content,
|
||||
'ticket': '{"version": "1.0"}',
|
||||
'tags': ['GAM', GAM_URL]
|
||||
}
|
||||
i = 5
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'tag':
|
||||
form_fields['tags'].append(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['name', 'title']:
|
||||
form_fields['title'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam printer ... print')
|
||||
form_files = {}
|
||||
if content[:4] == 'http':
|
||||
form_fields['content'] = content
|
||||
form_fields['contentType'] = 'url'
|
||||
else:
|
||||
filepath = content
|
||||
content = os.path.basename(content)
|
||||
mimetype = mimetypes.guess_type(filepath)[0]
|
||||
if mimetype is None:
|
||||
mimetype = 'application/octet-stream'
|
||||
filecontent = fileutils.read_file(filepath, mode='rb')
|
||||
form_files['content'] = {
|
||||
'filename': content,
|
||||
'content': filecontent,
|
||||
'mimetype': mimetype
|
||||
}
|
||||
#result = gapi.call(cp.printers(), u'submit', body=body)
|
||||
body, headers = encode_multipart(form_fields, form_files)
|
||||
#Get the printer first to make sure our OAuth access token is fresh
|
||||
gapi.call(cp.printers(), 'get', printerid=printer)
|
||||
_, result = cp._http.request(uri='https://www.google.com/cloudprint/submit',
|
||||
method='POST',
|
||||
body=body,
|
||||
headers=headers)
|
||||
result = json.loads(result.decode(UTF8))
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Submitted print job {result["job"]["id"]}')
|
||||
|
||||
|
||||
def doDeletePrintJob():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
job = sys.argv[2]
|
||||
result = gapi.call(cp.jobs(), 'delete', jobid=job)
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Print Job {job} deleted')
|
||||
|
||||
|
||||
def doCancelPrintJob():
|
||||
cp = buildGAPIObject('cloudprint')
|
||||
job = sys.argv[2]
|
||||
ssd = '{"state": {"type": "ABORTED", "user_action_cause": {"action_code": "CANCELLED"}}}'
|
||||
result = gapi.call(cp.jobs(), 'update', jobid=job, semantic_state_diff=ssd)
|
||||
checkCloudPrintResult(result)
|
||||
print(f'Print Job {job} cancelled')
|
||||
|
||||
|
||||
def checkCloudPrintResult(result):
|
||||
if isinstance(result, bytes):
|
||||
result = result.decode(UTF8)
|
||||
if isinstance(result, str):
|
||||
try:
|
||||
result = json.loads(result)
|
||||
except ValueError:
|
||||
controlflow.system_error_exit(3, f'unexpected response: {result}')
|
||||
if not result['success']:
|
||||
controlflow.system_error_exit(
|
||||
result['errorCode'], f'{result["errorCode"]}: {result["message"]}')
|
||||
|
||||
|
||||
def doProfile(users):
|
||||
cd = buildGAPIObject('directory')
|
||||
myarg = sys.argv[4].lower()
|
||||
@@ -7326,6 +6637,7 @@ def getUserAttributes(i, cd, updateCmd):
|
||||
i += 1
|
||||
need_password = True
|
||||
need_to_hash_password = True
|
||||
need_to_b64_decrypt_password = False
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg in ['firstname', 'givenname']:
|
||||
@@ -7366,13 +6678,17 @@ def getUserAttributes(i, cd, updateCmd):
|
||||
body['includeInGlobalAddressList'] = getBoolean(
|
||||
sys.argv[i + 1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['sha', 'sha1', 'sha-1']:
|
||||
elif myarg in ['sha', 'sha1', 'sha-1', 'base64-sha1']:
|
||||
body['hashFunction'] = 'SHA-1'
|
||||
need_to_hash_password = False
|
||||
if myarg == 'base64-sha1':
|
||||
need_to_b64_decrypt_password = True
|
||||
i += 1
|
||||
elif myarg == 'md5':
|
||||
elif myarg in ['md5', 'base64-md5']:
|
||||
body['hashFunction'] = 'MD5'
|
||||
need_to_hash_password = False
|
||||
if myarg == 'base64-md5':
|
||||
need_to_b64_decrypt_password = True
|
||||
i += 1
|
||||
elif myarg == 'crypt':
|
||||
body['hashFunction'] = 'crypt'
|
||||
@@ -7846,6 +7162,10 @@ def getUserAttributes(i, cd, updateCmd):
|
||||
if 'password' in body and need_to_hash_password:
|
||||
body['password'] = gen_sha512_hash(body['password'])
|
||||
body['hashFunction'] = 'crypt'
|
||||
elif 'password' in body and need_to_b64_decrypt_password:
|
||||
if body['password'].lower()[:5] in ['{md5}', '{sha}']:
|
||||
body['password'] = body['password'][5:]
|
||||
body['password'] = base64.b64decode(body['password']).hex()
|
||||
return body
|
||||
|
||||
|
||||
@@ -7890,34 +7210,36 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
|
||||
i=0,
|
||||
count=0):
|
||||
apis = GAMProjectAPIs[:]
|
||||
project_name = f'project:{projectId}'
|
||||
serveman = getService('servicemanagement', httpObj)
|
||||
project_name = f'projects/{projectId}'
|
||||
serveu = getService('serviceusage', httpObj)
|
||||
status = True
|
||||
if checkEnabled:
|
||||
try:
|
||||
services = gapi.get_all_pages(
|
||||
serveman.services(),
|
||||
serveu.services(),
|
||||
'list',
|
||||
'services',
|
||||
throw_reasons=[gapi_errors.ErrorReason.NOT_FOUND],
|
||||
consumerId=project_name,
|
||||
fields='nextPageToken,services(serviceName)')
|
||||
parent=project_name,
|
||||
filter='state:ENABLED',
|
||||
fields='nextPageToken,services(name)')
|
||||
jcount = len(services)
|
||||
print(
|
||||
f' Project: {projectId}, Check {jcount} APIs{currentCount(i, count)}'
|
||||
)
|
||||
j = 0
|
||||
for service in sorted(services, key=lambda k: k['serviceName']):
|
||||
for service in sorted(services, key=lambda k: k['name']):
|
||||
j += 1
|
||||
if 'serviceName' in service:
|
||||
if service['serviceName'] in apis:
|
||||
if 'name' in service:
|
||||
service_name = service['name'].split('/')[-1]
|
||||
if service_name in apis:
|
||||
print(
|
||||
f' API: {service["serviceName"]}, Already enabled{currentCount(j, jcount)}'
|
||||
f' API: {service_name}, Already enabled{currentCount(j, jcount)}'
|
||||
)
|
||||
apis.remove(service['serviceName'])
|
||||
apis.remove(service_name)
|
||||
else:
|
||||
print(
|
||||
f' API: {service["serviceName"]}, Already enabled (non-GAM which is fine){currentCount(j, jcount)}'
|
||||
f' API: {service_name}, Already enabled (non-GAM which is fine){currentCount(j, jcount)}'
|
||||
)
|
||||
except gapi_errors.GapiNotFoundError as e:
|
||||
print(
|
||||
@@ -7931,18 +7253,18 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
|
||||
)
|
||||
j = 0
|
||||
for api in apis:
|
||||
service_name = f'projects/{projectId}/services/{api}'
|
||||
j += 1
|
||||
while True:
|
||||
try:
|
||||
gapi.call(serveman.services(),
|
||||
gapi.call(serveu.services(),
|
||||
'enable',
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.FAILED_PRECONDITION,
|
||||
gapi_errors.ErrorReason.FORBIDDEN,
|
||||
gapi_errors.ErrorReason.PERMISSION_DENIED
|
||||
],
|
||||
serviceName=api,
|
||||
body={'consumerId': project_name})
|
||||
name=service_name)
|
||||
print(f' API: {api}, Enabled{currentCount(j, jcount)}')
|
||||
break
|
||||
except gapi_errors.GapiFailedPreconditionError as e:
|
||||
@@ -9372,7 +8694,7 @@ def doUpdateGroup():
|
||||
item.append(delivery)
|
||||
item.append(user_email)
|
||||
items.append(item)
|
||||
else:
|
||||
elif len(users_email) > 0:
|
||||
body = {
|
||||
'role':
|
||||
role,
|
||||
@@ -9498,7 +8820,7 @@ def doUpdateGroup():
|
||||
for user_email in users_email:
|
||||
items.append(
|
||||
['gam', 'update', 'group', group, 'remove', user_email])
|
||||
else:
|
||||
elif len(users_email) > 0:
|
||||
try:
|
||||
gapi.call(cd.members(),
|
||||
'delete',
|
||||
@@ -9532,7 +8854,7 @@ def doUpdateGroup():
|
||||
item.append(delivery)
|
||||
item.append(user_email)
|
||||
items.append(item)
|
||||
else:
|
||||
elif len(users_email) > 0:
|
||||
body = {}
|
||||
update_text = []
|
||||
if role:
|
||||
@@ -9617,7 +8939,7 @@ def doUpdateGroup():
|
||||
'gam', 'update', 'group', group, 'remove',
|
||||
user_email
|
||||
])
|
||||
else:
|
||||
elif len(users_email) > 0:
|
||||
try:
|
||||
gapi.call(cd.members(),
|
||||
'delete',
|
||||
@@ -12818,11 +12140,12 @@ def getUsersToModify(entity_type=None,
|
||||
]
|
||||
elif entity_type == 'users':
|
||||
users = entity.replace(',', ' ').split()
|
||||
elif entity_type in ['group', 'group_ns', 'group_susp']:
|
||||
elif entity_type in ['group', 'group_ns', 'group_susp', 'group_inde']:
|
||||
if entity_type == 'group_ns':
|
||||
checkSuspended = False
|
||||
elif entity_type == 'group_susp':
|
||||
checkSuspended = True
|
||||
includeDerivedMembership = entity_type == 'group_inde'
|
||||
got_uids = True
|
||||
group = entity
|
||||
if member_type is None:
|
||||
@@ -12839,16 +12162,18 @@ def getUsersToModify(entity_type=None,
|
||||
'...')
|
||||
validRoles, listRoles, listFields = _getRoleVerification(
|
||||
member_type, 'nextPageToken,members(email,id,type,status)')
|
||||
members = gapi.get_all_pages(cd.members(),
|
||||
'list',
|
||||
'members',
|
||||
page_message=page_message,
|
||||
groupKey=group,
|
||||
roles=listRoles,
|
||||
fields=listFields)
|
||||
members = gapi.get_all_pages(
|
||||
cd.members(),
|
||||
'list',
|
||||
'members',
|
||||
page_message=page_message,
|
||||
groupKey=group,
|
||||
roles=listRoles,
|
||||
includeDerivedMembership=includeDerivedMembership,
|
||||
fields=listFields)
|
||||
users = []
|
||||
for member in members:
|
||||
if ((not groupUserMembersOnly) or
|
||||
if ((not groupUserMembersOnly and not includeDerivedMembership) or
|
||||
(member['type'] == 'USER')) and _checkMemberRoleIsSuspended(
|
||||
member, validRoles, checkSuspended):
|
||||
users.append(member.get('email', member['id']))
|
||||
@@ -13236,11 +12561,6 @@ OAUTH2_SCOPES = [
|
||||
'https://www.googleapis.com/auth/classroom.guardianlinks.students'
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'Cloud Print API',
|
||||
'subscopes': [],
|
||||
'scopes': 'https://www.googleapis.com/auth/cloudprint'
|
||||
},
|
||||
{
|
||||
'name': 'Data Transfer API',
|
||||
'subscopes': ['readonly'],
|
||||
@@ -14034,7 +13354,7 @@ def ProcessGAMCommand(args):
|
||||
2,
|
||||
f'batch file: {filename}, not processed, {errors} error{["", "s"][errors != 1]}'
|
||||
)
|
||||
elif command == 'csv':
|
||||
elif command in ['csv', 'csvtest']:
|
||||
if httplib2.debuglevel > 0:
|
||||
controlflow.system_error_exit(
|
||||
1,
|
||||
@@ -14058,7 +13378,24 @@ def ProcessGAMCommand(args):
|
||||
items.append(['gam'] +
|
||||
processSubFields(GAM_argv, row, subFields))
|
||||
fileutils.close_file(f)
|
||||
run_batch(items)
|
||||
if command == 'csv':
|
||||
run_batch(items)
|
||||
else:
|
||||
print('The CSV file has the following headers:')
|
||||
for field in csvFile.fieldnames:
|
||||
print(f' {field}')
|
||||
print()
|
||||
num_items = min(len(items), 10)
|
||||
print(
|
||||
f'Here are the first {num_items} commands GAM will run (note that quoting may be lost/invalid in this output):\n'
|
||||
)
|
||||
for i in range(num_items):
|
||||
c = ' '.join([item if (item and
|
||||
(item.find(' ') == -1) and
|
||||
(item.find(',') == -1) and
|
||||
(item.find("'") == -1))
|
||||
else '"'+item+'"' for item in items[i]])
|
||||
print(f' {c}')
|
||||
sys.exit(0)
|
||||
elif command == 'version':
|
||||
doGAMVersion()
|
||||
@@ -14143,8 +13480,6 @@ def ProcessGAMCommand(args):
|
||||
doCreateOrUpdateUserSchema(True)
|
||||
elif argument in ['course', 'class']:
|
||||
doUpdateCourse()
|
||||
elif argument in ['printer', 'print']:
|
||||
doUpdatePrinter()
|
||||
elif argument == 'domain':
|
||||
doUpdateDomain()
|
||||
elif argument == 'customer':
|
||||
@@ -14192,8 +13527,6 @@ def ProcessGAMCommand(args):
|
||||
doGetUserSchema()
|
||||
elif argument in ['course', 'class']:
|
||||
doGetCourseInfo()
|
||||
elif argument in ['printer', 'print']:
|
||||
doGetPrinterInfo()
|
||||
elif argument in ['transfer', 'datatransfer']:
|
||||
doGetDataTransferInfo()
|
||||
elif argument == 'customer':
|
||||
@@ -14245,8 +13578,6 @@ def ProcessGAMCommand(args):
|
||||
doDelSchema()
|
||||
elif argument in ['course', 'class']:
|
||||
doDelCourse()
|
||||
elif argument in ['printer', 'printers']:
|
||||
doDelPrinter()
|
||||
elif argument == 'domain':
|
||||
doDelDomain()
|
||||
elif argument in ['domainalias', 'aliasdomain']:
|
||||
@@ -14325,10 +13656,6 @@ def ProcessGAMCommand(args):
|
||||
doPrintCourses()
|
||||
elif argument in ['courseparticipants', 'classparticipants']:
|
||||
doPrintCourseParticipants()
|
||||
elif argument == 'printers':
|
||||
doPrintPrinters()
|
||||
elif argument == 'printjobs':
|
||||
doPrintPrintJobs()
|
||||
elif argument in ['transfers', 'datatransfers']:
|
||||
doPrintDataTransfers()
|
||||
elif argument == 'transferapps':
|
||||
@@ -14426,35 +13753,6 @@ def ProcessGAMCommand(args):
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam calendar')
|
||||
sys.exit(0)
|
||||
elif command == 'printer':
|
||||
if sys.argv[2].lower() == 'register':
|
||||
doPrinterRegister()
|
||||
sys.exit(0)
|
||||
argument = sys.argv[3].lower()
|
||||
if argument == 'showacl':
|
||||
doPrinterShowACL()
|
||||
elif argument == 'add':
|
||||
doPrinterAddACL()
|
||||
elif argument in ['del', 'delete', 'remove']:
|
||||
doPrinterDelACL()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam printer...')
|
||||
sys.exit(0)
|
||||
elif command == 'printjob':
|
||||
argument = sys.argv[3].lower()
|
||||
if argument == 'delete':
|
||||
doDeletePrintJob()
|
||||
elif argument == 'cancel':
|
||||
doCancelPrintJob()
|
||||
elif argument == 'submit':
|
||||
doPrintJobSubmit()
|
||||
elif argument == 'fetch':
|
||||
doPrintJobFetch()
|
||||
elif argument == 'resubmit':
|
||||
doPrintJobResubmit()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam printjob')
|
||||
sys.exit(0)
|
||||
elif command == 'report':
|
||||
gapi_reports.showReport()
|
||||
sys.exit(0)
|
||||
|
||||
@@ -372,6 +372,7 @@ def getEventAttributes(i, calendarId, cal, body, action):
|
||||
# calendars are notified of changes
|
||||
sendUpdates = 'externalOnly'
|
||||
action = 'update' if body else 'add'
|
||||
timeZone = None
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']:
|
||||
|
||||
@@ -316,7 +316,9 @@ def showReport():
|
||||
eventName = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'user':
|
||||
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
userKey = sys.argv[i + 1].lower()
|
||||
if userKey != 'all':
|
||||
userKey = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['filter', 'filters']:
|
||||
filters = sys.argv[i + 1]
|
||||
|
||||
@@ -8,7 +8,7 @@ import platform
|
||||
import re
|
||||
|
||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||
GAM_VERSION = '5.10'
|
||||
GAM_VERSION = '5.11'
|
||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
@@ -26,12 +26,12 @@ GAM_PROJECT_FILEPATH = 'https://raw.githubusercontent.com/jay0lee/GAM/master/src
|
||||
true_values = ['on', 'yes', 'enabled', 'true', '1']
|
||||
false_values = ['off', 'no', 'disabled', 'false', '0']
|
||||
usergroup_types = [
|
||||
'user', 'users', 'group', 'group_ns', 'grooup_susp', 'ou', 'org', 'ou_ns',
|
||||
'org_ns', 'ou_susp', 'org_susp', 'ou_and_children', 'ou_and_child',
|
||||
'ou_and_children_ns', 'ou_and_child_ns', 'ou_and_children_susp',
|
||||
'ou_and_child_susp', 'query', 'queries', 'license', 'licenses', 'licence',
|
||||
'licences', 'file', 'csv', 'csvfile', 'all', 'cros', 'cros_sn', 'crosquery',
|
||||
'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
|
||||
'user', 'users', 'group', 'group_ns', 'group_susp', 'group_inde', 'ou',
|
||||
'org', 'ou_ns', 'org_ns', 'ou_susp', 'org_susp', 'ou_and_children',
|
||||
'ou_and_child', 'ou_and_children_ns', 'ou_and_child_ns',
|
||||
'ou_and_children_susp', 'ou_and_child_susp', 'query', 'queries', 'license',
|
||||
'licenses', 'licence', 'licences', 'file', 'csv', 'csvfile', 'all', 'cros',
|
||||
'cros_sn', 'crosquery', 'crosqueries', 'crosfile', 'croscsv', 'croscsvfile'
|
||||
]
|
||||
ERROR_PREFIX = 'ERROR: '
|
||||
WARNING_PREFIX = 'WARNING: '
|
||||
@@ -236,7 +236,6 @@ API_VER_MAPPING = {
|
||||
'appsactivity': 'v1',
|
||||
'calendar': 'v3',
|
||||
'classroom': 'v1',
|
||||
'cloudprint': 'v2',
|
||||
'cloudresourcemanager': 'v2',
|
||||
'cloudresourcemanagerv1': 'v1',
|
||||
'datatransfer': 'datatransfer_v1',
|
||||
@@ -253,6 +252,7 @@ API_VER_MAPPING = {
|
||||
'reports': 'reports_v1',
|
||||
'reseller': 'v1',
|
||||
'servicemanagement': 'v1',
|
||||
'serviceusage': 'v1',
|
||||
'sheets': 'v4',
|
||||
'siteVerification': 'v1',
|
||||
'storage': 'v1',
|
||||
|
||||
@@ -34,8 +34,10 @@ else
|
||||
mkdir python
|
||||
echo "RUNNING: apt update..."
|
||||
sudo apt-get -qq --yes update > /dev/null
|
||||
echo "RUNNING: apt dist-upgrade..."
|
||||
sudo apt-get -qq --yes dist-upgrade > /dev/null
|
||||
echo "RUNNING: apt upgrade..."
|
||||
sudo apt-mark hold openssh-server
|
||||
sudo apt-get --yes upgrade
|
||||
sudo apt-get --yes --with-new-pkgs upgrade
|
||||
echo "Installing build tools..."
|
||||
sudo apt-get -qq --yes install build-essential
|
||||
echo "Installing deps for python3"
|
||||
@@ -72,7 +74,8 @@ else
|
||||
echo "running configure with safe and unsafe"
|
||||
./configure $safe_flags $unsafe_flags > /dev/null
|
||||
fi
|
||||
make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
#make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
make -j$cpucount -s
|
||||
RESULT=$?
|
||||
echo "First make exited with $RESULT"
|
||||
if [ $RESULT != 0 ]; then
|
||||
|
||||
Reference in New Issue
Block a user