mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-23 15:41:36 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f233f7505c | ||
|
|
315367c80c | ||
|
|
aba602ff2a | ||
|
|
da8833bdc6 | ||
|
|
ed932cce55 | ||
|
|
37ade0059f | ||
|
|
7f6683e21d | ||
|
|
ca44a0dfb6 | ||
|
|
d9f85c600b | ||
|
|
c019a6d2e8 | ||
|
|
e8e25c352b | ||
|
|
1f75ac6112 | ||
|
|
0f111e6eaf | ||
|
|
8ee5b0ffdb | ||
|
|
8813f6c046 | ||
|
|
58fde587a8 | ||
|
|
06573991f6 | ||
|
|
597d3ac4b4 | ||
|
|
c62fa88d00 | ||
|
|
fffb847b5f | ||
|
|
fe69068bba |
@@ -1,9 +1,7 @@
|
|||||||
rmdir /q /s gam
|
rmdir /q /s gam
|
||||||
rmdir /q /s gam-64
|
rmdir /q /s gam-64
|
||||||
rmdir /q /s python-src-%1
|
|
||||||
rmdir /q /s build
|
rmdir /q /s build
|
||||||
rmdir /q /s dist
|
rmdir /q /s dist
|
||||||
del /q /f gam-%1-python-src.zip
|
|
||||||
del /q /f gam-%1-windows.zip
|
del /q /f gam-%1-windows.zip
|
||||||
del /q /f gam-%1-windows-x64.zip
|
del /q /f gam-%1-windows-x64.zip
|
||||||
|
|
||||||
|
|||||||
323
gam.py
323
gam.py
@@ -24,7 +24,7 @@ For more information, see http://code.google.com/p/google-apps-manager
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = u'Jay Lee <jay0lee@gmail.com>'
|
__author__ = u'Jay Lee <jay0lee@gmail.com>'
|
||||||
__version__ = u'3.32'
|
__version__ = u'3.4'
|
||||||
__license__ = u'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
__license__ = u'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||||
|
|
||||||
import sys, os, time, datetime, random, socket, csv, platform, re, calendar, base64, hashlib
|
import sys, os, time, datetime, random, socket, csv, platform, re, calendar, base64, hashlib
|
||||||
@@ -447,17 +447,17 @@ def callGAPIpages(service, function, items=u'items', nextPageToken=u'nextPageTok
|
|||||||
show_message = page_message
|
show_message = page_message
|
||||||
try:
|
try:
|
||||||
show_message = show_message.replace(u'%%num_items%%', str(page_items))
|
show_message = show_message.replace(u'%%num_items%%', str(page_items))
|
||||||
except KeyError:
|
except (IndexError, KeyError):
|
||||||
show_message = show_message.replace(u'%%num_items%%', '0')
|
show_message = show_message.replace(u'%%num_items%%', '0')
|
||||||
try:
|
try:
|
||||||
show_message = show_message.replace(u'%%total_items%%', str(total_items))
|
show_message = show_message.replace(u'%%total_items%%', str(total_items))
|
||||||
except KeyError:
|
except (IndexError, KeyError):
|
||||||
show_message = show_message.replace(u'%%total_items%%', '0')
|
show_message = show_message.replace(u'%%total_items%%', '0')
|
||||||
if message_attribute:
|
if message_attribute:
|
||||||
try:
|
try:
|
||||||
show_message = show_message.replace(u'%%first_item%%', str(this_page[items][0][message_attribute]))
|
show_message = show_message.replace(u'%%first_item%%', str(this_page[items][0][message_attribute]))
|
||||||
show_message = show_message.replace(u'%%last_item%%', str(this_page[items][-1][message_attribute]))
|
show_message = show_message.replace(u'%%last_item%%', str(this_page[items][-1][message_attribute]))
|
||||||
except KeyError:
|
except (IndexError, KeyError):
|
||||||
show_message = show_message.replace(u'%%first_item%%', '')
|
show_message = show_message.replace(u'%%first_item%%', '')
|
||||||
show_message = show_message.replace(u'%%last_item%%', '')
|
show_message = show_message.replace(u'%%last_item%%', '')
|
||||||
restart_line()
|
restart_line()
|
||||||
@@ -467,7 +467,7 @@ def callGAPIpages(service, function, items=u'items', nextPageToken=u'nextPageTok
|
|||||||
pageToken = this_page[nextPageToken]
|
pageToken = this_page[nextPageToken]
|
||||||
if pageToken == '':
|
if pageToken == '':
|
||||||
return all_pages
|
return all_pages
|
||||||
except KeyError:
|
except (IndexError, KeyError):
|
||||||
return all_pages
|
return all_pages
|
||||||
|
|
||||||
def getAPIVer(api):
|
def getAPIVer(api):
|
||||||
@@ -2214,6 +2214,7 @@ def downloadDriveFile(users):
|
|||||||
query = fileIds = None
|
query = fileIds = None
|
||||||
gdownload_format = u'openoffice'
|
gdownload_format = u'openoffice'
|
||||||
target_folder = getGamPath()
|
target_folder = getGamPath()
|
||||||
|
safe_filename_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
if sys.argv[i].lower() == u'id':
|
if sys.argv[i].lower() == u'id':
|
||||||
fileIds = [sys.argv[i+1],]
|
fileIds = [sys.argv[i+1],]
|
||||||
@@ -2263,7 +2264,7 @@ def downloadDriveFile(users):
|
|||||||
if fileIds[0].find('/') != -1:
|
if fileIds[0].find('/') != -1:
|
||||||
fileIds[0] = fileIds[0][:fileIds[0].find('/')]
|
fileIds[0] = fileIds[0][:fileIds[0].find('/')]
|
||||||
if not fileIds:
|
if not fileIds:
|
||||||
print u'No files to delete for %s' % user
|
print u'No files to download for %s' % user
|
||||||
i = 0
|
i = 0
|
||||||
for fileId in fileIds:
|
for fileId in fileIds:
|
||||||
extension = None
|
extension = None
|
||||||
@@ -2295,7 +2296,9 @@ def downloadDriveFile(users):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
break
|
break
|
||||||
filename = u'%s/%s' % (target_folder, result[u'title'])
|
file_title = result[u'title']
|
||||||
|
safe_file_title = ''.join(c for c in file_title if c in safe_filename_chars)
|
||||||
|
filename = u'%s/%s' % (target_folder, safe_file_title)
|
||||||
if extension and filename.lower()[:len(extension)] != extension:
|
if extension and filename.lower()[:len(extension)] != extension:
|
||||||
filename = u'%s%s' % (filename, extension)
|
filename = u'%s%s' % (filename, extension)
|
||||||
y = 0
|
y = 0
|
||||||
@@ -2664,7 +2667,7 @@ def showSendAs(users):
|
|||||||
print u''
|
print u''
|
||||||
|
|
||||||
def doLanguage(users):
|
def doLanguage(users):
|
||||||
language = sys.argv[4].lower()
|
language = sys.argv[4]
|
||||||
emailsettings = getEmailSettingsObject()
|
emailsettings = getEmailSettingsObject()
|
||||||
count = len(users)
|
count = len(users)
|
||||||
i = 1
|
i = 1
|
||||||
@@ -3270,6 +3273,104 @@ def getVacation(users):
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def doCreateDomain():
|
||||||
|
cd = buildGAPIObject(u'directory')
|
||||||
|
domain_name = sys.argv[3]
|
||||||
|
domain_type = sys.argv[4].upper()
|
||||||
|
if domain_type.replace(u'_', '') in [u'ALIAS', u'MIRROR', u'DOMAINALIAS', u'ALIAS_DOMAIN']:
|
||||||
|
domain_type = u'DOMAIN_ALIAS'
|
||||||
|
elif domain_type.replace(u'_', '') in [u'SECONDARY', u'SEPARATE', u'MULTIDOMAIN', u'MULTI']:
|
||||||
|
domain_type = u'MULTI_DOMAIN'
|
||||||
|
elif domain_type.replace(u'_', '') in [u'PRIMARY', u'PRIMARYDOMAIN', u'HOME']:
|
||||||
|
domain_type = u'PRIMARY'
|
||||||
|
elif domain_type != u'UNKNOWN':
|
||||||
|
print u'Error: domain type should be alias, secondary, primary or unknown. Got %s' % domain_type
|
||||||
|
sys.exit(4)
|
||||||
|
body = {u'domain_name': domain_name, u'domain_type': domain_type}
|
||||||
|
callGAPI(service=cd.domains(), function=u'insert', customerId=customerId, body=body)
|
||||||
|
print u'Added domain %s' % domain_name
|
||||||
|
|
||||||
|
def doDelSchema():
|
||||||
|
cd = buildGAPIObject(u'directory')
|
||||||
|
schemaKey = sys.argv[3]
|
||||||
|
callGAPI(service=cd.schemas(), function=u'delete', customerId=customerId, schemaKey=schemaKey)
|
||||||
|
print u'Deleted schema %s' % schemaKey
|
||||||
|
|
||||||
|
def doCreateOrUpdateUserSchema():
|
||||||
|
cd = buildGAPIObject(u'directory')
|
||||||
|
schemaName = sys.argv[3]
|
||||||
|
body = {u'schemaName': schemaName, u'fields': []}
|
||||||
|
i = 4
|
||||||
|
while i < len(sys.argv):
|
||||||
|
if sys.argv[i] in [u'field']:
|
||||||
|
a_field = {u'fieldName': sys.argv[i+1]}
|
||||||
|
i += 2
|
||||||
|
while True:
|
||||||
|
if sys.argv[i].lower() in [u'type']:
|
||||||
|
a_field[u'fieldType'] = sys.argv[i+1].upper()
|
||||||
|
if a_field[u'fieldType'] not in [u'BOOL', u'DOUBLE', u'EMAIL', u'INT64', u'PHONE', u'STRING']:
|
||||||
|
print 'Error: type must be bool, double, email, int64, phone or string. Got %s' % a_field[u'fieldType']
|
||||||
|
sys.exit(3)
|
||||||
|
i += 2
|
||||||
|
elif sys.argv[i].lower() in [u'multivalued']:
|
||||||
|
a_field[u'multiValued'] = True
|
||||||
|
i += 1
|
||||||
|
elif sys.argv[i].lower() in [u'indexed']:
|
||||||
|
a_field[u'indexed'] = True
|
||||||
|
i += 1
|
||||||
|
elif sys.argv[i].lower() in [u'restricted']:
|
||||||
|
a_field[u'readAccessType'] = u'ADMINS_AND_SELF'
|
||||||
|
i += 1
|
||||||
|
elif sys.argv[i].lower() in [u'range']:
|
||||||
|
a_field[u'numericIndexingSpec'] = {u'minValue': sys.argv[i+1], u'maxValue': sys.argv[i+2]}
|
||||||
|
i += 3
|
||||||
|
elif sys.argv[i].lower() in [u'endfield']:
|
||||||
|
body[u'fields'].append(a_field)
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print 'Error: %s is not a valid argument to gam create schema' % sys.argv[i]
|
||||||
|
sys.exit(4)
|
||||||
|
if sys.argv[1].lower() == u'create':
|
||||||
|
result = callGAPI(service=cd.schemas(), function=u'insert', customerId=customerId, body=body)
|
||||||
|
print 'Created user schema %s' % result[u'schemaName']
|
||||||
|
elif sys.argv[1].lower() == u'update':
|
||||||
|
result = callGAPI(service=cd.schemas(), function=u'update', customerId=customerId, body=body, schemaKey=schemaName)
|
||||||
|
print 'Updated user schema %s' % result[u'schemaName']
|
||||||
|
|
||||||
|
def doPrintUserSchemas():
|
||||||
|
cd = buildGAPIObject(u'directory')
|
||||||
|
schemas = callGAPI(service=cd.schemas(), function=u'list', customerId=customerId)
|
||||||
|
for schema in schemas[u'schemas']:
|
||||||
|
print u'Schema: %s' % schema[u'schemaName']
|
||||||
|
for a_key in schema.keys():
|
||||||
|
if a_key not in [u'schemaName', u'fields', u'etag', u'kind']:
|
||||||
|
print '%s: %s' % (a_key, schema[a_key])
|
||||||
|
print
|
||||||
|
for field in schema[u'fields']:
|
||||||
|
print u' Field: %s' % field[u'fieldName']
|
||||||
|
for a_key in field.keys():
|
||||||
|
if a_key not in [u'fieldName', u'kind', u'etag']:
|
||||||
|
print ' %s: %s' % (a_key, field[a_key])
|
||||||
|
print
|
||||||
|
print
|
||||||
|
|
||||||
|
def doGetUserSchema():
|
||||||
|
cd = buildGAPIObject(u'directory')
|
||||||
|
schemaKey = sys.argv[3]
|
||||||
|
schema = callGAPI(service=cd.schemas(), function=u'get', customerId=customerId, schemaKey=schemaKey)
|
||||||
|
print u'Schema: %s' % schema[u'schemaName']
|
||||||
|
for a_key in schema.keys():
|
||||||
|
if a_key not in [u'schemaName', u'fields', u'etag', u'kind']:
|
||||||
|
print '%s: %s' % (a_key, schema[a_key])
|
||||||
|
print
|
||||||
|
for field in schema[u'fields']:
|
||||||
|
print u' Field: %s' % field[u'fieldName']
|
||||||
|
for a_key in field.keys():
|
||||||
|
if a_key not in [u'fieldName', u'kind', u'etag']:
|
||||||
|
print ' %s: %s' % (a_key, field[a_key])
|
||||||
|
print
|
||||||
|
|
||||||
def doCreateUser():
|
def doCreateUser():
|
||||||
cd = buildGAPIObject(u'directory')
|
cd = buildGAPIObject(u'directory')
|
||||||
body = dict()
|
body = dict()
|
||||||
@@ -3567,9 +3668,33 @@ def doCreateUser():
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
body[u'externalIds'] = [externalid,]
|
body[u'externalIds'] = [externalid,]
|
||||||
i += 1
|
i += 1
|
||||||
|
# else:
|
||||||
|
# showUsage()
|
||||||
|
# sys.exit(2)
|
||||||
else:
|
else:
|
||||||
showUsage()
|
if u'customSchemas' not in body:
|
||||||
sys.exit(2)
|
body[u'customSchemas'] = {}
|
||||||
|
try:
|
||||||
|
(schemaName, fieldName) = sys.argv[i].split(u'.')
|
||||||
|
except ValueError:
|
||||||
|
print 'Error: %s is not a valid create user argument or custom schema name.' % sys.argv[i]
|
||||||
|
sys.exit(3)
|
||||||
|
field_value = sys.argv[i+1]
|
||||||
|
is_multivalue = False
|
||||||
|
if field_value.lower() in [u'multivalue', u'multivalued', u'value']:
|
||||||
|
is_multivalue = True
|
||||||
|
field_value = sys.argv[i+2]
|
||||||
|
if schemaName not in body[u'customSchemas']:
|
||||||
|
body[u'customSchemas'][schemaName] = {}
|
||||||
|
if is_multivalue:
|
||||||
|
if fieldName not in body[u'customSchemas'][schemaName]:
|
||||||
|
body[u'customSchemas'][schemaName][fieldName] = []
|
||||||
|
body[u'customSchemas'][schemaName][fieldName].append({u'value': field_value})
|
||||||
|
else:
|
||||||
|
body[u'customSchemas'][schemaName][fieldName] = field_value
|
||||||
|
i += 2
|
||||||
|
if is_multivalue:
|
||||||
|
i += 1
|
||||||
if not gotFirstName:
|
if not gotFirstName:
|
||||||
body[u'name'][u'givenName'] = u'Unknown'
|
body[u'name'][u'givenName'] = u'Unknown'
|
||||||
if not gotLastName:
|
if not gotLastName:
|
||||||
@@ -4039,11 +4164,36 @@ def doUpdateUser(users):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
body[u'externalIds'] = [externalid,]
|
body[u'externalIds'] = [externalid,]
|
||||||
i += 1
|
i += 1
|
||||||
|
# else:
|
||||||
|
# showUsage()
|
||||||
|
# print u''
|
||||||
|
# print u'Error: didn\'t expect %s command at position %s' % (sys.argv[i], i)
|
||||||
|
# sys.exit(2)
|
||||||
else:
|
else:
|
||||||
showUsage()
|
do_update_user = True
|
||||||
print u''
|
if u'customSchemas' not in body:
|
||||||
print u'Error: didn\'t expect %s command at position %s' % (sys.argv[i], i)
|
body[u'customSchemas'] = {}
|
||||||
sys.exit(2)
|
try:
|
||||||
|
(schemaName, fieldName) = sys.argv[i].split(u'.')
|
||||||
|
except ValueError:
|
||||||
|
print u'Error: %s is not a valid user update argument or custom schema name' % sys.argv[i]
|
||||||
|
sys.exit(3)
|
||||||
|
field_value = sys.argv[i+1]
|
||||||
|
is_multivalue = False
|
||||||
|
if field_value.lower() == u'multivalue':
|
||||||
|
is_multivalue = True
|
||||||
|
field_value = sys.argv[i+2]
|
||||||
|
if schemaName not in body[u'customSchemas']:
|
||||||
|
body[u'customSchemas'][schemaName] = {}
|
||||||
|
if is_multivalue:
|
||||||
|
if fieldName not in body[u'customSchemas'][schemaName]:
|
||||||
|
body[u'customSchemas'][schemaName][fieldName] = []
|
||||||
|
body[u'customSchemas'][schemaName][fieldName].append({u'value': field_value})
|
||||||
|
else:
|
||||||
|
body[u'customSchemas'][schemaName][fieldName] = field_value
|
||||||
|
i += 2
|
||||||
|
if is_multivalue:
|
||||||
|
i += 1
|
||||||
if gotPassword and not (isSHA1 or isMD5 or isCrypt or nohash):
|
if gotPassword and not (isSHA1 or isMD5 or isCrypt or nohash):
|
||||||
body[u'password'] = gen_sha512_hash(body[u'password'])
|
body[u'password'] = gen_sha512_hash(body[u'password'])
|
||||||
body[u'hashFunction'] = u'crypt'
|
body[u'hashFunction'] = u'crypt'
|
||||||
@@ -4185,7 +4335,7 @@ def doUpdateGroup():
|
|||||||
gs_object = buildDiscoveryObject(u'groupssettings')
|
gs_object = buildDiscoveryObject(u'groupssettings')
|
||||||
matches_gs_setting = False
|
matches_gs_setting = False
|
||||||
for (attrib, params) in gs_object[u'schemas'][u'Groups'][u'properties'].items():
|
for (attrib, params) in gs_object[u'schemas'][u'Groups'][u'properties'].items():
|
||||||
if attrib in [u'kind', u'etag', u'email', u'name', u'description']:
|
if attrib in [u'kind', u'etag', u'email']:
|
||||||
continue
|
continue
|
||||||
if sys.argv[i].lower().replace(u'_', u'') == attrib.lower():
|
if sys.argv[i].lower().replace(u'_', u'') == attrib.lower():
|
||||||
matches_gs_setting = True
|
matches_gs_setting = True
|
||||||
@@ -4211,7 +4361,7 @@ def doUpdateGroup():
|
|||||||
value = u'false'
|
value = u'false'
|
||||||
break
|
break
|
||||||
if not matches_gs_setting:
|
if not matches_gs_setting:
|
||||||
print u'ERROR: %s is not a valid argument for "gam create group..."' % sys.argv[i]
|
print u'ERROR: %s is not a valid argument for "gam update group..."' % sys.argv[i]
|
||||||
sys.exit(9)
|
sys.exit(9)
|
||||||
gs_body[attrib] = value
|
gs_body[attrib] = value
|
||||||
use_gs_api = True
|
use_gs_api = True
|
||||||
@@ -4472,7 +4622,9 @@ def doGetUserInfo(user_email=None):
|
|||||||
user_email = user_email[4:]
|
user_email = user_email[4:]
|
||||||
elif user_email.find(u'@') == -1:
|
elif user_email.find(u'@') == -1:
|
||||||
user_email = u'%s@%s' % (user_email, domain)
|
user_email = u'%s@%s' % (user_email, domain)
|
||||||
getAliases = getGroups = getLicenses = True
|
getSchemas = getAliases = getGroups = getLicenses = True
|
||||||
|
projection = u'full'
|
||||||
|
customFieldMask = viewType = None
|
||||||
i = 4
|
i = 4
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
if sys.argv[i].lower() == u'noaliases':
|
if sys.argv[i].lower() == u'noaliases':
|
||||||
@@ -4484,10 +4636,23 @@ def doGetUserInfo(user_email=None):
|
|||||||
elif sys.argv[i].lower() == u'nolicenses':
|
elif sys.argv[i].lower() == u'nolicenses':
|
||||||
getLicenses = False
|
getLicenses = False
|
||||||
i += 1
|
i += 1
|
||||||
|
elif sys.argv[i].lower() == u'noschemas':
|
||||||
|
getSchemas = False
|
||||||
|
projection = u'basic'
|
||||||
|
i += 1
|
||||||
|
elif sys.argv[i].lower() == u'schemas':
|
||||||
|
getSchemas = True
|
||||||
|
projection = u'custom'
|
||||||
|
customFieldMask = sys.argv[i+1]
|
||||||
|
i += 2
|
||||||
|
elif sys.argv[i].lower() == u'userview':
|
||||||
|
viewType = u'domain_public'
|
||||||
|
getGroups = getLicenses = False
|
||||||
|
i += 1
|
||||||
else:
|
else:
|
||||||
print u'%s is not a valid argument for gam info user' % sys.argv[i]
|
print u'%s is not a valid argument for gam info user' % sys.argv[i]
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
user = callGAPI(service=cd.users(), function=u'get', userKey=user_email)
|
user = callGAPI(service=cd.users(), function=u'get', userKey=user_email, projection=projection, customFieldMask=customFieldMask, viewType=viewType)
|
||||||
print u'User: %s' % user[u'primaryEmail']
|
print u'User: %s' % user[u'primaryEmail']
|
||||||
if u'name' in user and u'givenName' in user[u'name']:
|
if u'name' in user and u'givenName' in user[u'name']:
|
||||||
print u'First Name: %s' % user[u'name'][u'givenName']
|
print u'First Name: %s' % user[u'name'][u'givenName']
|
||||||
@@ -4586,6 +4751,19 @@ def doGetUserInfo(user_email=None):
|
|||||||
else:
|
else:
|
||||||
print u' %s: %s' % (key, id[key])
|
print u' %s: %s' % (key, id[key])
|
||||||
print u''
|
print u''
|
||||||
|
if getSchemas:
|
||||||
|
print u'Custom Schemas:'
|
||||||
|
if u'customSchemas' in user:
|
||||||
|
for schema in user[u'customSchemas']:
|
||||||
|
print u' Schema: %s' % schema
|
||||||
|
for field in user[u'customSchemas'][schema]:
|
||||||
|
if type(user[u'customSchemas'][schema][field]) is list:
|
||||||
|
print ' %s:' % field
|
||||||
|
for an_item in user[u'customSchemas'][schema][field]:
|
||||||
|
print ' %s' % an_item[u'value']
|
||||||
|
else:
|
||||||
|
print u' %s: %s' % (field, user[u'customSchemas'][schema][field])
|
||||||
|
print
|
||||||
if getAliases:
|
if getAliases:
|
||||||
if u'aliases' in user:
|
if u'aliases' in user:
|
||||||
print u'Email Aliases:'
|
print u'Email Aliases:'
|
||||||
@@ -4860,28 +5038,34 @@ def doSiteVerifyAttempt():
|
|||||||
print u'Method: %s' % verify_data[u'method']
|
print u'Method: %s' % verify_data[u'method']
|
||||||
print u'Token: %s' % verify_data[u'token']
|
print u'Token: %s' % verify_data[u'token']
|
||||||
if verify_data[u'method'] == u'DNS_CNAME':
|
if verify_data[u'method'] == u'DNS_CNAME':
|
||||||
import dns.resolver
|
|
||||||
resolver = dns.resolver.Resolver()
|
|
||||||
resolver.nameservers = nameservers=[u'8.8.8.8', u'8.8.4.4']
|
|
||||||
cname_token = verify_data[u'token']
|
|
||||||
cname_list = cname_token.split(u' ')
|
|
||||||
cname_subdomain = cname_list[0]
|
|
||||||
try:
|
try:
|
||||||
answers = resolver.query(u'%s.%s' % (cname_subdomain, a_domain), u'A')
|
import dns.resolver
|
||||||
for answer in answers:
|
resolver = dns.resolver.Resolver()
|
||||||
print u'DNS Record: %s' % answer
|
resolver.nameservers = nameservers=[u'8.8.8.8', u'8.8.4.4']
|
||||||
except dns.resolver.NXDOMAIN:
|
cname_token = verify_data[u'token']
|
||||||
print u'ERROR: No such domain found in DNS!'
|
cname_list = cname_token.split(u' ')
|
||||||
|
cname_subdomain = cname_list[0]
|
||||||
|
try:
|
||||||
|
answers = resolver.query(u'%s.%s' % (cname_subdomain, a_domain), u'A')
|
||||||
|
for answer in answers:
|
||||||
|
print u'DNS Record: %s' % answer
|
||||||
|
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||||
|
print u'ERROR: No such domain found in DNS!'
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
elif verify_data[u'method'] == u'DNS_TXT':
|
elif verify_data[u'method'] == u'DNS_TXT':
|
||||||
import dns.resolver
|
|
||||||
resolver = dns.resolver.Resolver()
|
|
||||||
resolver.nameservers = nameservers=[u'8.8.8.8', u'8.8.4.4']
|
|
||||||
try:
|
try:
|
||||||
answers = resolver.query(a_domain, u'TXT')
|
import dns.resolver
|
||||||
for answer in answers:
|
resolver = dns.resolver.Resolver()
|
||||||
print u'DNS Record: %s' % str(answer).replace(u'"', u'')
|
resolver.nameservers = nameservers=[u'8.8.8.8', u'8.8.4.4']
|
||||||
except dns.resolver.NXDOMAIN:
|
try:
|
||||||
print u'ERROR: no such domain found in DNS!'
|
answers = resolver.query(a_domain, u'TXT')
|
||||||
|
for answer in answers:
|
||||||
|
print u'DNS Record: %s' % str(answer).replace(u'"', u'')
|
||||||
|
except dns.resolver.NXDOMAIN:
|
||||||
|
print u'ERROR: no such domain found in DNS!'
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
print u'SUCCESS!'
|
print u'SUCCESS!'
|
||||||
print u'Verified: %s' % verify_result[u'site'][u'identifier']
|
print u'Verified: %s' % verify_result[u'site'][u'identifier']
|
||||||
@@ -5286,7 +5470,8 @@ def doGetDomainInfo():
|
|||||||
if customerId != u'my_customer':
|
if customerId != u'my_customer':
|
||||||
customer_id = customerId
|
customer_id = customerId
|
||||||
else:
|
else:
|
||||||
customer_id = callGAPI(service=cd.users(), function=u'list', fields=u'users(customerId)', customer=customerId, maxResults=1)[u'users'][0][u'customerId']
|
result = callGAPI(service=cd.users(), function=u'list', fields=u'users(customerId)', customer=customerId, maxResults=1, sortOrder=u'DESCENDING')
|
||||||
|
customer_id = result[u'users'][0][u'customerId']
|
||||||
print u'Customer ID: %s' % customer_id
|
print u'Customer ID: %s' % customer_id
|
||||||
default_language = callGAPI(service=adm.defaultLanguage(), function=u'get', domainName=domain)
|
default_language = callGAPI(service=adm.defaultLanguage(), function=u'get', domainName=domain)
|
||||||
print u'Default Language: %s' % default_language[u'entry'][u'apps$property'][0][u'value']
|
print u'Default Language: %s' % default_language[u'entry'][u'apps$property'][0][u'value']
|
||||||
@@ -5523,14 +5708,24 @@ def doPrintUsers():
|
|||||||
customer = customerId
|
customer = customerId
|
||||||
domain = None
|
domain = None
|
||||||
query = None
|
query = None
|
||||||
getGroupFeed = getLicenseFeed = False
|
projection = u'basic'
|
||||||
|
customFieldMask = None
|
||||||
|
getGroupFeed = getLicenseFeed = email_parts = False
|
||||||
todrive = False
|
todrive = False
|
||||||
deleted_only = orderBy = sortOrder = None
|
viewType = deleted_only = orderBy = sortOrder = None
|
||||||
i = 3
|
i = 3
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
if sys.argv[i].lower() == u'allfields':
|
if sys.argv[i].lower() == u'allfields':
|
||||||
fields = None
|
fields = None
|
||||||
i += 1
|
i += 1
|
||||||
|
elif sys.argv[i].lower() == u'custom':
|
||||||
|
user_fields.append(u'customSchemas')
|
||||||
|
if sys.argv[i+1].lower() == u'all':
|
||||||
|
projection = u'full'
|
||||||
|
else:
|
||||||
|
projection = u'custom'
|
||||||
|
customFieldMask = sys.argv[i+1]
|
||||||
|
i += 2
|
||||||
elif sys.argv[i].lower() == u'todrive':
|
elif sys.argv[i].lower() == u'todrive':
|
||||||
todrive = True
|
todrive = True
|
||||||
i += 1
|
i += 1
|
||||||
@@ -5547,6 +5742,9 @@ def doPrintUsers():
|
|||||||
elif orderBy.lower() in [u'givenname', u'firstname']:
|
elif orderBy.lower() in [u'givenname', u'firstname']:
|
||||||
orderBy= u'givenName'
|
orderBy= u'givenName'
|
||||||
i += 2
|
i += 2
|
||||||
|
elif sys.argv[i].lower() == u'userview':
|
||||||
|
viewType = u'domain_public'
|
||||||
|
i += 1
|
||||||
elif sys.argv[i].lower() in [u'ascending', u'descending']:
|
elif sys.argv[i].lower() in [u'ascending', u'descending']:
|
||||||
sortOrder = sys.argv[i].upper()
|
sortOrder = sys.argv[i].upper()
|
||||||
i += 1
|
i += 1
|
||||||
@@ -5631,7 +5829,7 @@ def doPrintUsers():
|
|||||||
fields = u'nextPageToken,users(%s)' % u','.join(user_fields)
|
fields = u'nextPageToken,users(%s)' % u','.join(user_fields)
|
||||||
sys.stderr.write(u"Getting all users in Google Apps account (may take some time on a large account)...\n")
|
sys.stderr.write(u"Getting all users in Google Apps account (may take some time on a large account)...\n")
|
||||||
page_message = u'Got %%total_items%% users: %%first_item%% - %%last_item%%\n'
|
page_message = u'Got %%total_items%% users: %%first_item%% - %%last_item%%\n'
|
||||||
all_users = callGAPIpages(service=cd.users(), function=u'list', items=u'users', page_message=page_message, message_attribute=u'primaryEmail', customer=customer, domain=domain, fields=fields, showDeleted=deleted_only, maxResults=500, orderBy=orderBy, sortOrder=sortOrder, query=query)
|
all_users = callGAPIpages(service=cd.users(), function=u'list', items=u'users', page_message=page_message, message_attribute=u'primaryEmail', customer=customer, domain=domain, fields=fields, showDeleted=deleted_only, maxResults=500, orderBy=orderBy, sortOrder=sortOrder, viewType=viewType, query=query, projection=projection, customFieldMask=customFieldMask)
|
||||||
titles = []
|
titles = []
|
||||||
attributes = []
|
attributes = []
|
||||||
for user in all_users:
|
for user in all_users:
|
||||||
@@ -6620,7 +6818,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, return_uids=Fa
|
|||||||
if not silent:
|
if not silent:
|
||||||
sys.stderr.write(u"Getting %s of %s (may take some time for large groups)..." % (member_type_message, group))
|
sys.stderr.write(u"Getting %s of %s (may take some time for large groups)..." % (member_type_message, group))
|
||||||
page_message = u'Got %%%%total_items%%%% %s...' % member_type_message
|
page_message = u'Got %%%%total_items%%%% %s...' % member_type_message
|
||||||
members = callGAPIpages(service=cd.members(), function=u'list', page_message=page_message, groupKey=group, roles=member_type, fields=u'nextPageToken,members(email,id)')
|
members = callGAPIpages(service=cd.members(), function=u'list', items=u'members', page_message=page_message, groupKey=group, roles=member_type, fields=u'nextPageToken,members(email,id)')
|
||||||
users = []
|
users = []
|
||||||
for member in members:
|
for member in members:
|
||||||
if return_uids:
|
if return_uids:
|
||||||
@@ -6829,11 +7027,14 @@ found at:
|
|||||||
|
|
||||||
%s
|
%s
|
||||||
|
|
||||||
with information from the APIs Console <https://code.google.com/apis/console>.
|
with information from the APIs Console <https://cloud.google.com/console>.
|
||||||
|
|
||||||
|
See https://code.google.com/p/google-apps-manager/wiki/CreatingClientSecretsFile
|
||||||
|
for instructions.
|
||||||
|
|
||||||
""" % CLIENT_SECRETS
|
""" % CLIENT_SECRETS
|
||||||
|
|
||||||
selected_scopes = [u'*'] * 19
|
selected_scopes = [u'*'] * 20
|
||||||
menu = u'''Select the authorized scopes for this token. Include a 'r' to grant read-only
|
menu = u'''Select the authorized scopes for this token. Include a 'r' to grant read-only
|
||||||
access or an 'a' to grant action-only access.
|
access or an 'a' to grant action-only access.
|
||||||
|
|
||||||
@@ -6856,10 +7057,11 @@ access or an 'a' to grant action-only access.
|
|||||||
[%s] 16) Notifications Directory API
|
[%s] 16) Notifications Directory API
|
||||||
[%s] 17) Site Verification API
|
[%s] 17) Site Verification API
|
||||||
(%s) 18) IMAP/SMTP Access (send notifications to admin)
|
(%s) 18) IMAP/SMTP Access (send notifications to admin)
|
||||||
|
(%s) 19) User Schemas (supports read-only)
|
||||||
|
|
||||||
19) Select all scopes
|
20) Select all scopes
|
||||||
20) Unselect all scopes
|
21) Unselect all scopes
|
||||||
21) Continue
|
22) Continue
|
||||||
'''
|
'''
|
||||||
os.system([u'clear', u'cls'][os.name == u'nt'])
|
os.system([u'clear', u'cls'][os.name == u'nt'])
|
||||||
while True:
|
while True:
|
||||||
@@ -6867,7 +7069,7 @@ access or an 'a' to grant action-only access.
|
|||||||
try:
|
try:
|
||||||
if selection.lower().find(u'r') != -1:
|
if selection.lower().find(u'r') != -1:
|
||||||
selection = int(selection.lower().replace(u'r', u''))
|
selection = int(selection.lower().replace(u'r', u''))
|
||||||
if selection not in [0, 1, 2, 3, 4, 10]:
|
if selection not in [0, 1, 2, 3, 4, 10, 19]:
|
||||||
os.system([u'clear', u'cls'][os.name == u'nt'])
|
os.system([u'clear', u'cls'][os.name == u'nt'])
|
||||||
print u'THAT SCOPE DOES NOT SUPPORT READ-ONLY MODE!\n'
|
print u'THAT SCOPE DOES NOT SUPPORT READ-ONLY MODE!\n'
|
||||||
continue
|
continue
|
||||||
@@ -6879,18 +7081,18 @@ access or an 'a' to grant action-only access.
|
|||||||
print u'THAT SCOPE DOES NOT SUPPORT ACTION-ONLY MODE!\n'
|
print u'THAT SCOPE DOES NOT SUPPORT ACTION-ONLY MODE!\n'
|
||||||
continue
|
continue
|
||||||
selected_scopes[selection] = u'A'
|
selected_scopes[selection] = u'A'
|
||||||
elif int(selection) > -1 and int(selection) <= 18:
|
elif int(selection) > -1 and int(selection) <= 19:
|
||||||
if selected_scopes[int(selection)] == u' ':
|
if selected_scopes[int(selection)] == u' ':
|
||||||
selected_scopes[int(selection)] = u'*'
|
selected_scopes[int(selection)] = u'*'
|
||||||
else:
|
else:
|
||||||
selected_scopes[int(selection)] = u' '
|
selected_scopes[int(selection)] = u' '
|
||||||
elif selection == u'19':
|
|
||||||
for i in range(0, len(selected_scopes)):
|
|
||||||
selected_scopes[i] = u'*'
|
|
||||||
elif selection == u'20':
|
elif selection == u'20':
|
||||||
for i in range(0, len(selected_scopes)):
|
for i in range(0, len(selected_scopes)):
|
||||||
selected_scopes[i] = u' '
|
selected_scopes[i] = u'*'
|
||||||
elif selection == u'21':
|
elif selection == u'21':
|
||||||
|
for i in range(0, len(selected_scopes)):
|
||||||
|
selected_scopes[i] = u' '
|
||||||
|
elif selection == u'22':
|
||||||
at_least_one = False
|
at_least_one = False
|
||||||
for i in range(0, len(selected_scopes)):
|
for i in range(0, len(selected_scopes)):
|
||||||
if selected_scopes[i] in [u'*', u'R', u'A']:
|
if selected_scopes[i] in [u'*', u'R', u'A']:
|
||||||
@@ -6929,7 +7131,8 @@ access or an 'a' to grant action-only access.
|
|||||||
u'https://www.googleapis.com/auth/admin.directory.user.security', # User Security Directory API
|
u'https://www.googleapis.com/auth/admin.directory.user.security', # User Security Directory API
|
||||||
u'https://www.googleapis.com/auth/admin.directory.notifications', # Notifications Directory API
|
u'https://www.googleapis.com/auth/admin.directory.notifications', # Notifications Directory API
|
||||||
u'https://www.googleapis.com/auth/siteverification', # Site Verification API
|
u'https://www.googleapis.com/auth/siteverification', # Site Verification API
|
||||||
u'https://mail.google.com/'] # IMAP/SMTP authentication for admin notifications
|
u'https://mail.google.com/', # IMAP/SMTP authentication for admin notifications
|
||||||
|
u'https://www.googleapis.com/auth/admin.directory.userschema'] # Customer User Schema
|
||||||
if incremental_auth:
|
if incremental_auth:
|
||||||
scopes = []
|
scopes = []
|
||||||
else:
|
else:
|
||||||
@@ -7067,12 +7270,14 @@ try:
|
|||||||
doCreateGroup()
|
doCreateGroup()
|
||||||
elif sys.argv[2].lower() in [u'nickname', u'alias']:
|
elif sys.argv[2].lower() in [u'nickname', u'alias']:
|
||||||
doCreateAlias()
|
doCreateAlias()
|
||||||
elif sys.argv[2].lower() == u'org':
|
elif sys.argv[2].lower() in [u'org', 'ou']:
|
||||||
doCreateOrg()
|
doCreateOrg()
|
||||||
elif sys.argv[2].lower() == u'resource':
|
elif sys.argv[2].lower() == u'resource':
|
||||||
doCreateResource()
|
doCreateResource()
|
||||||
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
||||||
doSiteVerifyShow()
|
doSiteVerifyShow()
|
||||||
|
elif sys.argv[2].lower() in [u'schema']:
|
||||||
|
doCreateOrUpdateUserSchema()
|
||||||
else:
|
else:
|
||||||
print u'Error: invalid argument to "gam create..."'
|
print u'Error: invalid argument to "gam create..."'
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -7098,6 +7303,8 @@ try:
|
|||||||
doUpdateNotification()
|
doUpdateNotification()
|
||||||
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
||||||
doSiteVerifyAttempt()
|
doSiteVerifyAttempt()
|
||||||
|
elif sys.argv[2].lower() in [u'schema', u'schemas']:
|
||||||
|
doCreateOrUpdateUserSchema()
|
||||||
else:
|
else:
|
||||||
showUsage()
|
showUsage()
|
||||||
print u'Error: invalid argument to "gam update..."'
|
print u'Error: invalid argument to "gam update..."'
|
||||||
@@ -7124,6 +7331,8 @@ try:
|
|||||||
doGetNotifications()
|
doGetNotifications()
|
||||||
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
||||||
doGetSiteVerifications()
|
doGetSiteVerifications()
|
||||||
|
elif sys.argv[2].lower() in [u'schema', u'schemas']:
|
||||||
|
doGetUserSchema()
|
||||||
else:
|
else:
|
||||||
print u'Error: invalid argument to "gam info..."'
|
print u'Error: invalid argument to "gam info..."'
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -7145,6 +7354,8 @@ try:
|
|||||||
doDeleteNotification()
|
doDeleteNotification()
|
||||||
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
elif sys.argv[2].lower() in [u'verify', u'verification']:
|
||||||
doDelSiteVerify()
|
doDelSiteVerify()
|
||||||
|
elif sys.argv[2].lower() in [u'schema', u'schemas']:
|
||||||
|
doDelSchema()
|
||||||
else:
|
else:
|
||||||
print u'Error: invalid argument to "gam delete"'
|
print u'Error: invalid argument to "gam delete"'
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -7222,6 +7433,8 @@ try:
|
|||||||
doPrintLicenses()
|
doPrintLicenses()
|
||||||
elif sys.argv[2].lower() in [u'token', u'tokens']:
|
elif sys.argv[2].lower() in [u'token', u'tokens']:
|
||||||
doPrintTokens()
|
doPrintTokens()
|
||||||
|
elif sys.argv[2].lower() in [u'schema', u'schemas']:
|
||||||
|
doPrintUserSchemas()
|
||||||
else:
|
else:
|
||||||
print u'Error: invalid argument to "gam print..."'
|
print u'Error: invalid argument to "gam print..."'
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|||||||
@@ -1269,7 +1269,7 @@ class OAuth2WebServerFlow(Flow):
|
|||||||
'client_secret': self.client_secret,
|
'client_secret': self.client_secret,
|
||||||
'code': code,
|
'code': code,
|
||||||
'redirect_uri': self.redirect_uri,
|
'redirect_uri': self.redirect_uri,
|
||||||
'scope': self.scope,
|
# 'scope': self.scope,
|
||||||
})
|
})
|
||||||
headers = {
|
headers = {
|
||||||
'content-type': 'application/x-www-form-urlencoded',
|
'content-type': 'application/x-www-form-urlencoded',
|
||||||
|
|||||||
@@ -1,147 +1,265 @@
|
|||||||
# Early, and incomplete implementation of -04.
|
#!/usr/bin/env python
|
||||||
#
|
|
||||||
|
"""
|
||||||
|
URI Template (RFC6570) Processor
|
||||||
|
"""
|
||||||
|
|
||||||
|
__copyright__ = """\
|
||||||
|
Copyright 2011-2013 Joe Gregorio
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import urllib
|
try:
|
||||||
|
from urllib.parse import quote
|
||||||
|
except ImportError:
|
||||||
|
from urllib import quote
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = "0.6"
|
||||||
|
|
||||||
RESERVED = ":/?#[]@!$&'()*+,;="
|
RESERVED = ":/?#[]@!$&'()*+,;="
|
||||||
OPERATOR = "+./;?|!@"
|
OPERATOR = "+#./;?&|!@"
|
||||||
EXPLODE = "*+"
|
|
||||||
MODIFIER = ":^"
|
MODIFIER = ":^"
|
||||||
TEMPLATE = re.compile(r"{(?P<operator>[\+\./;\?|!@])?(?P<varlist>[^}]+)}", re.UNICODE)
|
TEMPLATE = re.compile("{([^\}]+)}")
|
||||||
VAR = re.compile(r"^(?P<varname>[^=\+\*:\^]+)((?P<explode>[\+\*])|(?P<partial>[:\^]-?[0-9]+))?(=(?P<default>.*))?$", re.UNICODE)
|
|
||||||
|
|
||||||
def _tostring(varname, value, explode, operator, safe=""):
|
|
||||||
if type(value) == type([]):
|
|
||||||
if explode == "+":
|
|
||||||
return ",".join([varname + "." + urllib.quote(x, safe) for x in value])
|
|
||||||
else:
|
|
||||||
return ",".join([urllib.quote(x, safe) for x in value])
|
|
||||||
if type(value) == type({}):
|
|
||||||
keys = value.keys()
|
|
||||||
keys.sort()
|
|
||||||
if explode == "+":
|
|
||||||
return ",".join([varname + "." + urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
||||||
else:
|
|
||||||
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
||||||
else:
|
|
||||||
return urllib.quote(value, safe)
|
|
||||||
|
|
||||||
|
|
||||||
def _tostring_path(varname, value, explode, operator, safe=""):
|
def variables(template):
|
||||||
joiner = operator
|
'''Returns the set of keywords in a uri template'''
|
||||||
if type(value) == type([]):
|
vars = set()
|
||||||
if explode == "+":
|
for varlist in TEMPLATE.findall(template):
|
||||||
return joiner.join([varname + "." + urllib.quote(x, safe) for x in value])
|
if varlist[0] in OPERATOR:
|
||||||
elif explode == "*":
|
varlist = varlist[1:]
|
||||||
return joiner.join([urllib.quote(x, safe) for x in value])
|
varspecs = varlist.split(',')
|
||||||
else:
|
for var in varspecs:
|
||||||
return ",".join([urllib.quote(x, safe) for x in value])
|
# handle prefix values
|
||||||
elif type(value) == type({}):
|
var = var.split(':')[0]
|
||||||
keys = value.keys()
|
# handle composite values
|
||||||
keys.sort()
|
if var.endswith('*'):
|
||||||
if explode == "+":
|
var = var[:-1]
|
||||||
return joiner.join([varname + "." + urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
|
vars.add(var)
|
||||||
elif explode == "*":
|
return vars
|
||||||
return joiner.join([urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
|
|
||||||
else:
|
|
||||||
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
|
||||||
else:
|
|
||||||
if value:
|
|
||||||
return urllib.quote(value, safe)
|
|
||||||
else:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def _tostring_query(varname, value, explode, operator, safe=""):
|
|
||||||
joiner = operator
|
def _quote(value, safe, prefix=None):
|
||||||
varprefix = ""
|
if prefix is not None:
|
||||||
if operator == "?":
|
return quote(str(value)[:prefix], safe)
|
||||||
joiner = "&"
|
return quote(str(value), safe)
|
||||||
varprefix = varname + "="
|
|
||||||
if type(value) == type([]):
|
|
||||||
if 0 == len(value):
|
def _tostring(varname, value, explode, prefix, operator, safe=""):
|
||||||
return ""
|
if isinstance(value, list):
|
||||||
if explode == "+":
|
return ",".join([_quote(x, safe) for x in value])
|
||||||
return joiner.join([varname + "=" + urllib.quote(x, safe) for x in value])
|
if isinstance(value, dict):
|
||||||
elif explode == "*":
|
keys = sorted(value.keys())
|
||||||
return joiner.join([urllib.quote(x, safe) for x in value])
|
if explode:
|
||||||
|
return ",".join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
else:
|
||||||
|
return ",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
elif value is None:
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
return varprefix + ",".join([urllib.quote(x, safe) for x in value])
|
return _quote(value, safe, prefix)
|
||||||
elif type(value) == type({}):
|
|
||||||
if 0 == len(value):
|
|
||||||
return ""
|
def _tostring_path(varname, value, explode, prefix, operator, safe=""):
|
||||||
keys = value.keys()
|
joiner = operator
|
||||||
keys.sort()
|
if isinstance(value, list):
|
||||||
if explode == "+":
|
if explode:
|
||||||
return joiner.join([varname + "." + urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
out = [_quote(x, safe) for x in value if value is not None]
|
||||||
elif explode == "*":
|
else:
|
||||||
return joiner.join([urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
|
joiner = ","
|
||||||
|
out = [_quote(x, safe) for x in value if value is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
out = [_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) for key in keys \
|
||||||
|
if value[key] is not None]
|
||||||
|
else:
|
||||||
|
joiner = ","
|
||||||
|
out = [_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys if value[key] is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
elif value is None:
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
return varprefix + ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
|
return _quote(value, safe, prefix)
|
||||||
else:
|
|
||||||
if value:
|
|
||||||
return varname + "=" + urllib.quote(value, safe)
|
def _tostring_semi(varname, value, explode, prefix, operator, safe=""):
|
||||||
|
joiner = operator
|
||||||
|
if operator == "?":
|
||||||
|
joiner = "&"
|
||||||
|
if isinstance(value, list):
|
||||||
|
if explode:
|
||||||
|
out = [varname + "=" + _quote(x, safe) \
|
||||||
|
for x in value if x is not None]
|
||||||
|
if out:
|
||||||
|
return joiner.join(out)
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return varname + "=" + ",".join([_quote(x, safe) \
|
||||||
|
for x in value])
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
return joiner.join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys if key is not None])
|
||||||
|
else:
|
||||||
|
return varname + "=" + ",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys \
|
||||||
|
if key is not None])
|
||||||
else:
|
else:
|
||||||
return varname
|
if value is None:
|
||||||
|
return
|
||||||
|
elif value:
|
||||||
|
return (varname + "=" + _quote(value, safe, prefix))
|
||||||
|
else:
|
||||||
|
return varname
|
||||||
|
|
||||||
|
|
||||||
|
def _tostring_query(varname, value, explode, prefix, operator, safe=""):
|
||||||
|
joiner = operator
|
||||||
|
if operator in ["?", "&"]:
|
||||||
|
joiner = "&"
|
||||||
|
if isinstance(value, list):
|
||||||
|
if 0 == len(value):
|
||||||
|
return None
|
||||||
|
if explode:
|
||||||
|
return joiner.join([varname + "=" + _quote(x, safe) \
|
||||||
|
for x in value])
|
||||||
|
else:
|
||||||
|
return (varname + "=" + ",".join([_quote(x, safe) \
|
||||||
|
for x in value]))
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
if 0 == len(value):
|
||||||
|
return None
|
||||||
|
keys = sorted(value.keys())
|
||||||
|
if explode:
|
||||||
|
return joiner.join([_quote(key, safe) + "=" + \
|
||||||
|
_quote(value[key], safe) \
|
||||||
|
for key in keys])
|
||||||
|
else:
|
||||||
|
return varname + "=" + \
|
||||||
|
",".join([_quote(key, safe) + "," + \
|
||||||
|
_quote(value[key], safe) for key in keys])
|
||||||
|
else:
|
||||||
|
if value is None:
|
||||||
|
return
|
||||||
|
elif value:
|
||||||
|
return (varname + "=" + _quote(value, safe, prefix))
|
||||||
|
else:
|
||||||
|
return (varname + "=")
|
||||||
|
|
||||||
|
|
||||||
TOSTRING = {
|
TOSTRING = {
|
||||||
"" : _tostring,
|
"" : _tostring,
|
||||||
"+": _tostring,
|
"+": _tostring,
|
||||||
";": _tostring_query,
|
"#": _tostring,
|
||||||
|
";": _tostring_semi,
|
||||||
"?": _tostring_query,
|
"?": _tostring_query,
|
||||||
|
"&": _tostring_query,
|
||||||
"/": _tostring_path,
|
"/": _tostring_path,
|
||||||
".": _tostring_path,
|
".": _tostring_path,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def expand(template, vars):
|
def expand(template, variables):
|
||||||
def _sub(match):
|
"""
|
||||||
groupdict = match.groupdict()
|
Expand template as a URI Template using variables.
|
||||||
operator = groupdict.get('operator')
|
"""
|
||||||
if operator is None:
|
def _sub(match):
|
||||||
operator = ''
|
expression = match.group(1)
|
||||||
varlist = groupdict.get('varlist')
|
operator = ""
|
||||||
|
if expression[0] in OPERATOR:
|
||||||
|
operator = expression[0]
|
||||||
|
varlist = expression[1:]
|
||||||
|
else:
|
||||||
|
varlist = expression
|
||||||
|
|
||||||
safe = "@"
|
safe = ""
|
||||||
if operator == '+':
|
if operator in ["+", "#"]:
|
||||||
safe = RESERVED
|
safe = RESERVED
|
||||||
varspecs = varlist.split(",")
|
varspecs = varlist.split(",")
|
||||||
varnames = []
|
varnames = []
|
||||||
defaults = {}
|
defaults = {}
|
||||||
for varspec in varspecs:
|
for varspec in varspecs:
|
||||||
m = VAR.search(varspec)
|
default = None
|
||||||
groupdict = m.groupdict()
|
explode = False
|
||||||
varname = groupdict.get('varname')
|
prefix = None
|
||||||
explode = groupdict.get('explode')
|
if "=" in varspec:
|
||||||
partial = groupdict.get('partial')
|
varname, default = tuple(varspec.split("=", 1))
|
||||||
default = groupdict.get('default')
|
else:
|
||||||
if default:
|
varname = varspec
|
||||||
defaults[varname] = default
|
if varname[-1] == "*":
|
||||||
varnames.append((varname, explode, partial))
|
explode = True
|
||||||
|
varname = varname[:-1]
|
||||||
|
elif ":" in varname:
|
||||||
|
try:
|
||||||
|
prefix = int(varname[varname.index(":")+1:])
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("non-integer prefix '{0}'".format(
|
||||||
|
varname[varname.index(":")+1:]))
|
||||||
|
varname = varname[:varname.index(":")]
|
||||||
|
if default:
|
||||||
|
defaults[varname] = default
|
||||||
|
varnames.append((varname, explode, prefix))
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
joiner = operator
|
joiner = operator
|
||||||
prefix = operator
|
start = operator
|
||||||
if operator == "+":
|
if operator == "+":
|
||||||
prefix = ""
|
start = ""
|
||||||
joiner = ","
|
joiner = ","
|
||||||
if operator == "?":
|
if operator == "#":
|
||||||
joiner = "&"
|
joiner = ","
|
||||||
if operator == "":
|
if operator == "?":
|
||||||
joiner = ","
|
joiner = "&"
|
||||||
for varname, explode, partial in varnames:
|
if operator == "&":
|
||||||
if varname in vars:
|
start = "&"
|
||||||
value = vars[varname]
|
if operator == "":
|
||||||
#if not value and (type(value) == type({}) or type(value) == type([])) and varname in defaults:
|
joiner = ","
|
||||||
if not value and value != "" and varname in defaults:
|
for varname, explode, prefix in varnames:
|
||||||
value = defaults[varname]
|
if varname in variables:
|
||||||
elif varname in defaults:
|
value = variables[varname]
|
||||||
value = defaults[varname]
|
if not value and value != "" and varname in defaults:
|
||||||
else:
|
value = defaults[varname]
|
||||||
continue
|
elif varname in defaults:
|
||||||
retval.append(TOSTRING[operator](varname, value, explode, operator, safe=safe))
|
value = defaults[varname]
|
||||||
if "".join(retval):
|
else:
|
||||||
return prefix + joiner.join(retval)
|
continue
|
||||||
else:
|
expanded = TOSTRING[operator](
|
||||||
return ""
|
varname, value, explode, prefix, operator, safe=safe)
|
||||||
|
if expanded is not None:
|
||||||
|
retval.append(expanded)
|
||||||
|
if len(retval) > 0:
|
||||||
|
return start + joiner.join(retval)
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
return TEMPLATE.sub(_sub, template)
|
return TEMPLATE.sub(_sub, template)
|
||||||
|
|||||||
35
whatsnew.txt
35
whatsnew.txt
@@ -1,3 +1,36 @@
|
|||||||
|
GAM 3.4 "Oktoberfest"
|
||||||
|
-Support for creating and setting custom user schemas http://goo.gl/M9rQrI
|
||||||
|
-End user view of print users and user info commands.
|
||||||
|
-fix updating name/description for groups
|
||||||
|
-fix groups sync commands
|
||||||
|
-gam print groups members no longer fails on zero member groups
|
||||||
|
-make sure downloaded Drive file names are safe for OS filenames.
|
||||||
|
-gam info domain should no longer crash on getting customer ID
|
||||||
|
-other minor bug fixes
|
||||||
|
|
||||||
|
GAM 3.32
|
||||||
|
-fix service account json files downloaded from new cloud console don't work with GAM
|
||||||
|
-use the new Gmail API for label command. Offers ability to rename labels and show/hide labels from the label and message list.
|
||||||
|
-copy Google drive files with commands like gam user <email> update drivefile id <fileid> copy
|
||||||
|
-print only one SKU for licenses with commands like gam print licenses sku vault
|
||||||
|
-short license names for Google Apps Message Security (gams), Google Apps Unlimtied (gau) and Google Vault Former Employee (vfe)
|
||||||
|
|
||||||
|
GAM 3.31
|
||||||
|
-New command "gam user delete aliases" clears all aliases for user.
|
||||||
|
-"gam update user email vfe" will rename user to vfe.oldname.XXXXX@olddomain.com
|
||||||
|
-fix delete photo command
|
||||||
|
-fix password update/create for Windows builds
|
||||||
|
|
||||||
|
GAM 3.3
|
||||||
|
-Major rewrites of "gam report" and "gam print users". Note that CSV headers have changed. Better performance and print users now supports "rich" fields like organization, phone, relations, etc.
|
||||||
|
-"gam report drive" now works for Google Apps Unlimited domains.
|
||||||
|
-"gam info user" now shows the user's licenses. Use "nolicenses" to prevent showing licenses.
|
||||||
|
-fix "gam print licenses" fails in large domains by reducing page size from 1000 to 100
|
||||||
|
-GAM now sends a sha-512 salted hashed password on user create and update for better security.
|
||||||
|
-cleanup unused features of old GData library.
|
||||||
|
-fix for Drive file downloading default format.
|
||||||
|
-upgrade to httplib v0.9 which may help with httplib.BadStatus errors.
|
||||||
|
|
||||||
GAM 3.21
|
GAM 3.21
|
||||||
-Fix crash when attempting to perform Drive operations for users with Drive service disabled.
|
-Fix crash when attempting to perform Drive operations for users with Drive service disabled.
|
||||||
-Fix "gam info org" only prints first 100 users in org.
|
-Fix "gam info org" only prints first 100 users in org.
|
||||||
@@ -222,4 +255,4 @@ GAM 1.8
|
|||||||
|
|
||||||
-The settings filter command http://code.google.com/p/google-apps-manager/wiki/ExamplesEmailSettings#Create_a_Filter now has more actions including forward, star, trash and never send to spam.
|
-The settings filter command http://code.google.com/p/google-apps-manager/wiki/ExamplesEmailSettings#Create_a_Filter now has more actions including forward, star, trash and never send to spam.
|
||||||
|
|
||||||
-Downloading Audit Exports now has partial resume support. GAM will not re-download files that already exist on the local drive. If a large export download fails you should delete the last file GAM was working on since it's incomplete and then restart the process, GAM will pick up with the last file.
|
-Downloading Audit Exports now has partial resume support. GAM will not re-download files that already exist on the local drive. If a large export download fails you should delete the last file GAM was working on since it's incomplete and then restart the process, GAM will pick up with the last file.
|
||||||
|
|||||||
Reference in New Issue
Block a user