From 38399c0e1e216374c026b6e807e83bcf05397e8b Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sun, 3 Apr 2016 20:44:34 -0700 Subject: [PATCH 01/80] Improve info/update instance Print address fields in logical order in gam info instance Allow multiple address fields to be entered in a single gam update instance command --- src/gam.py | 97 +++++++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/gam.py b/src/gam.py index 48cb4b28..35628dbb 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7092,46 +7092,56 @@ def doDeprovUser(users): print u'No Tokens' print u'Done deprovisioning %s' % user +CUSTOMER_ARGUMENT_CHOICES = [u'adminsecondaryemail', u'language', u'name', u'phone', u'address'] + +POSTAL_FIELDS_ARGUMENT_MAP = { + u'contact': u'contactName', u'contactname': u'contactName', + u'address1': u'addressLine1', u'addressline1': u'addressLine1', + u'address2': u'addressLine2', u'addressline2': u'addressLine2', + u'address3': u'addressLine3', u'addressline3': u'addressLine3', + u'locality': u'locality', + u'name': u'organizationName', u'organizationname': u'organizationName', + u'region': u'region', + u'postalcode': u'postalCode', + u'country': u'countryCode', u'countrycode': u'countryCode', + } + def doUpdateInstance(): adminObj = getAdminSettingsObject() cd = buildGAPIObject(u'directory') - body = {} - if GC_Values[GC_CUSTOMER_ID] != MY_CUSTOMER: - customerKey = GC_Values[GC_CUSTOMER_ID] - else: - customerKey = MY_CUSTOMER - command = sys.argv[3].lower() - if command == u'language': - body[u'language'] = sys.argv[4] - elif command == u'name': - body[u'postalAddress'] = {u'organizationName': sys.argv[4]} - elif command == u'admin_secondary_email': - body[u'alternateEmail'] = sys.argv[4] - elif command == u'phone': - body[u'phoneNumber'] = sys.argv[4] - elif command == u'contact_name': - body[u'postalAddress'] = {u'contactName': sys.argv[4]} - elif command == u'locality': - body[u'postalAddress'] = {u'locality': sys.argv[4]} - elif command == u'region': - body[u'postalAddress'] = {u'region': sys.argv[4]} - elif command == u'country': - body[u'postalAddress'] = {u'countryCode': sys.argv[4]} - elif command == u'postal_code': - body[u'postalAddress'] = {u'postalCode': sys.argv[4]} - elif command == u'address1': - body[u'postalAddress'] = {u'addressLine1': sys.argv[4]} - elif command == u'address2': - body[u'postalAddress'] = {u'addressLine2': sys.argv[4]} - elif command == u'address3': - body[u'postalAddress'] = {u'addressLine3': sys.argv[4]} + command = sys.argv[3].lower().replace(u'_', u'') + i = 4 + if command in CUSTOMER_ARGUMENT_CHOICES: + body = {} + if command == u'adminsecondaryemail': + body[u'alternateEmail'] = sys.argv[i] + elif command == u'language': + body[u'language'] = sys.argv[i] + elif command == u'phone': + body[u'phoneNumber'] = sys.argv[i] + elif command == u'name': + body.setdefault(u'postalAddress', {}) + body[u'organizationName'] = sys.argv[i] + elif command == u'address': + body.setdefault(u'postalAddress', {}) + while i < len(sys.argv): + myarg = sys.argv[i].lower().replace(u'_', u'') + if myarg in POSTAL_FIELDS_ARGUMENT_MAP: + value = sys.argv[i+1] + if not value: + value = None + body[u'postalAddress'][POSTAL_FIELDS_ARGUMENT_MAP[myarg]] = value + i += 2 + else: + print u'ERROR: unknown option for "gam update instance address ...": %s' % sys.argv[i] + sys.exit(2) + callGAPI(cd.customers(), u'update', customerKey=GC_Values[GC_CUSTOMER_ID], body=body) elif command == u'logo': - logoFile = sys.argv[4] + logoFile = sys.argv[i] logoImage = readFile(logoFile) callGData(service=adminObj, function=u'UpdateDomainLogo', logoImage=logoImage) - elif command == u'sso_settings': + elif command == u'ssosettings': enableSSO = samlSignonUri = samlLogoutUri = changePasswordUri = ssoWhitelist = useDomainSpecificIssuer = None - i = 4 while i < len(sys.argv): if sys.argv[i].lower() == u'enabled': if sys.argv[i+1].lower() == u'true': @@ -7164,21 +7174,21 @@ def doUpdateInstance(): sys.exit(2) i += 2 else: - print u'ERROR: unknown option for "gam update domain sso_settings...": %s' % sys.argv[i] + print u'ERROR: unknown option for "gam update instance sso_settings...": %s' % sys.argv[i] sys.exit(2) callGData(service=adminObj, function=u'UpdateSSOSettings', enableSSO=enableSSO, samlSignonUri=samlSignonUri, samlLogoutUri=samlLogoutUri, changePasswordUri=changePasswordUri, ssoWhitelist=ssoWhitelist, useDomainSpecificIssuer=useDomainSpecificIssuer) - elif command == u'sso_key': - keyFile = sys.argv[4] + elif command == u'ssokey': + keyFile = sys.argv[i] keyData = readFile(keyFile) callGData(service=adminObj, function=u'UpdateSSOKey', signingKey=keyData) else: print u'ERROR: %s is not a valid argument for "gam update instance"' % command sys.exit(2) - if body != {}: - callGAPI(cd.customers(), u'update', customerKey=customerKey, body=body) + +POSTAL_ADDRESS_FIELDS = [u'contactName', u'organizationName', u'addressLine1', u'addressLine2', u'addressLine3', u'locality', u'region', u'postalCode', u'countryCode'] def doGetInstanceInfo(): adm = buildGAPIObject(u'admin-settings') @@ -7188,11 +7198,7 @@ def doGetInstanceInfo(): geturl(url, target_file) sys.exit(0) cd = buildGAPIObject(u'directory') - if GC_Values[GC_CUSTOMER_ID] != MY_CUSTOMER: - customerKey = GC_Values[GC_CUSTOMER_ID] - else: - customerKey = MY_CUSTOMER - customer_info = callGAPI(cd.customers(), u'get', customerKey=customerKey) + customer_info = callGAPI(cd.customers(), u'get', customerKey=GC_Values[GC_CUSTOMER_ID]) customerId = customer_info[u'id'] primaryDomain = customer_info[u'customerDomain'] print u'Customer ID: %s' % customerId @@ -7203,8 +7209,9 @@ def doGetInstanceInfo(): print u'Default Language: %s' % customer_info[u'language'] if u'postalAddress' in customer_info: print u'Address:' - for (key, value) in customer_info[u'postalAddress'].items(): - print u' %s: %s' % (key, value) + for field in POSTAL_ADDRESS_FIELDS: + if field in customer_info[u'postalAddress']: + print u' %s: %s' % (field, customer_info[u'postalAddress'][field]) if u'phoneNumber' in customer_info: print u'Phone: %s' % customer_info[u'phoneNumber'] print u'Admin Secondary Email: %s' % customer_info[u'alternateEmail'] From e527cd42a16e89fc76cce5a74d028f4abdbc5b50 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sun, 3 Apr 2016 21:02:37 -0700 Subject: [PATCH 02/80] Change POSTAL to ADDRESS --- src/gam.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/gam.py b/src/gam.py index 35628dbb..ef4cf83a 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7094,13 +7094,13 @@ def doDeprovUser(users): CUSTOMER_ARGUMENT_CHOICES = [u'adminsecondaryemail', u'language', u'name', u'phone', u'address'] -POSTAL_FIELDS_ARGUMENT_MAP = { +ADDRESS_FIELDS_ARGUMENT_MAP = { u'contact': u'contactName', u'contactname': u'contactName', + u'name': u'organizationName', u'organizationname': u'organizationName', u'address1': u'addressLine1', u'addressline1': u'addressLine1', u'address2': u'addressLine2', u'addressline2': u'addressLine2', u'address3': u'addressLine3', u'addressline3': u'addressLine3', u'locality': u'locality', - u'name': u'organizationName', u'organizationname': u'organizationName', u'region': u'region', u'postalcode': u'postalCode', u'country': u'countryCode', u'countrycode': u'countryCode', @@ -7126,11 +7126,11 @@ def doUpdateInstance(): body.setdefault(u'postalAddress', {}) while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') - if myarg in POSTAL_FIELDS_ARGUMENT_MAP: + if myarg in ADDRESS_FIELDS_ARGUMENT_MAP: value = sys.argv[i+1] if not value: value = None - body[u'postalAddress'][POSTAL_FIELDS_ARGUMENT_MAP[myarg]] = value + body[u'postalAddress'][ADDRESS_FIELDS_ARGUMENT_MAP[myarg]] = value i += 2 else: print u'ERROR: unknown option for "gam update instance address ...": %s' % sys.argv[i] @@ -7188,7 +7188,7 @@ def doUpdateInstance(): print u'ERROR: %s is not a valid argument for "gam update instance"' % command sys.exit(2) -POSTAL_ADDRESS_FIELDS = [u'contactName', u'organizationName', u'addressLine1', u'addressLine2', u'addressLine3', u'locality', u'region', u'postalCode', u'countryCode'] +ADDRESS_FIELDS = [u'contactName', u'organizationName', u'addressLine1', u'addressLine2', u'addressLine3', u'locality', u'region', u'postalCode', u'countryCode'] def doGetInstanceInfo(): adm = buildGAPIObject(u'admin-settings') @@ -7209,7 +7209,7 @@ def doGetInstanceInfo(): print u'Default Language: %s' % customer_info[u'language'] if u'postalAddress' in customer_info: print u'Address:' - for field in POSTAL_ADDRESS_FIELDS: + for field in ADDRESS_FIELDS: if field in customer_info[u'postalAddress']: print u' %s: %s' % (field, customer_info[u'postalAddress'][field]) if u'phoneNumber' in customer_info: From 2debf9507a3c9401d6949b0302d9ecdfbe22fa1b Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sun, 3 Apr 2016 21:09:33 -0700 Subject: [PATCH 03/80] Fix errors --- src/gam.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gam.py b/src/gam.py index ef4cf83a..6b880ab6 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7120,10 +7120,9 @@ def doUpdateInstance(): elif command == u'phone': body[u'phoneNumber'] = sys.argv[i] elif command == u'name': - body.setdefault(u'postalAddress', {}) - body[u'organizationName'] = sys.argv[i] + body[u'postalAddress'] = {u'organizationName': sys.argv[i]} elif command == u'address': - body.setdefault(u'postalAddress', {}) + body[u'postalAddress'] = {} while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') if myarg in ADDRESS_FIELDS_ARGUMENT_MAP: From d47f5967e9106f0a082ba6ef766b3071075e7394 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sun, 3 Apr 2016 22:50:11 -0700 Subject: [PATCH 04/80] Improve info/update customer, revert some update instance code Move print customer info code from getInstanceInfo to getCustomerInfo; getInstanceInfo calls getCustomerInfo doUpdateCustomer already had code added to doUpdateInstance, remove from doUpdateInstance --- src/gam.py | 130 +++++++++++++++++++---------------------------------- 1 file changed, 47 insertions(+), 83 deletions(-) diff --git a/src/gam.py b/src/gam.py index 6b880ab6..7e7bfedc 100755 --- a/src/gam.py +++ b/src/gam.py @@ -1467,10 +1467,38 @@ def doGetDomainAliasInfo(): result[u'creationTime'] = unicode(datetime.datetime.fromtimestamp(int(result[u'creationTime'])/1000)) print_json(None, result) +ADDRESS_FIELDS_PRINT_ORDER = [u'contactName', u'organizationName', u'addressLine1', u'addressLine2', u'addressLine3', u'locality', u'region', u'postalCode', u'countryCode'] + def doGetCustomerInfo(): cd = buildGAPIObject(u'directory') customer_info = callGAPI(service=cd.customers(), function=u'get', customerKey=GC_Values[GC_CUSTOMER_ID]) - print_json(None, customer_info) + print u'Customer ID: %s' % customer_info[u'id'] + print u'Primary Domain: %s' % customer_info[u'customerDomain'] + result = callGAPI(cd.domains(), u'get', + customer=customer_info[u'id'], domainName=customer_info[u'customerDomain'], fields=u'verified') + print u'Primary Domain Verified: %s' % result[u'verified'] + print u'Customer Creation Time: %s' % customer_info[u'customerCreationTime'] + print u'Default Language: %s' % customer_info[u'language'] + if u'postalAddress' in customer_info: + print u'Address:' + for field in ADDRESS_FIELDS_PRINT_ORDER: + if field in customer_info[u'postalAddress']: + print u' %s: %s' % (field, customer_info[u'postalAddress'][field]) + if u'phoneNumber' in customer_info: + print u'Phone: %s' % customer_info[u'phoneNumber'] + print u'Admin Secondary Email: %s' % customer_info[u'alternateEmail'] + +ADDRESS_FIELDS_ARGUMENT_MAP = { + u'contact': u'contactName', u'contactname': u'contactName', + u'name': u'organizationName', u'organizationname': u'organizationName', + u'address1': u'addressLine1', u'addressline1': u'addressLine1', + u'address2': u'addressLine2', u'addressline2': u'addressLine2', + u'address3': u'addressLine3', u'addressline3': u'addressLine3', + u'locality': u'locality', + u'region': u'region', + u'postalcode': u'postalCode', + u'country': u'countryCode', u'countrycode': u'countryCode', + } def doUpdateCustomer(): cd = buildGAPIObject(u'directory') @@ -1478,27 +1506,12 @@ def doUpdateCustomer(): i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') - if myarg == u'alternateemail': - body[u'alternateEmail'] = sys.argv[i+1] + if myarg in ADDRESS_FIELDS_ARGUMENT_MAP: + body.setdefault(u'postalAddress', {}) + body[u'postalAddress'][ADDRESS_FIELDS_ARGUMENT_MAP[myarg]] = sys.argv[i+1] i += 2 - elif myarg in [u'contactname', u'organizationname', u'locality', u'region', u'countrycode', u'addressline1', u'addressline2', u'addressline3', u'postalcode']: - if u'postalAddress' not in body: - body[u'postalAddress'] = {} - if myarg == u'contactname': - myarg = u'contactName' - elif myarg == u'organizationname': - myarg = u'organizationName' - elif myarg == u'countrycode': - myarg = u'countryCode' - elif myarg == u'addressline1': - myarg = u'addressLine1' - elif myarg == u'addressline2': - myarg = u'addressLine2' - elif myarg == u'addressline3': - myarg = u'addressLine3' - elif myarg == u'postalcode': - myarg = u'postalCode' - body[u'postalAddress'][myarg] = sys.argv[i+1] + elif myarg in [u'adminsecondaryemail', u'alternateemail']: + body[u'alternateEmail'] = sys.argv[i+1] i += 2 elif myarg in [u'phone', u'phonenumber']: body[u'phoneNumber'] = sys.argv[i+1] @@ -7092,54 +7105,24 @@ def doDeprovUser(users): print u'No Tokens' print u'Done deprovisioning %s' % user -CUSTOMER_ARGUMENT_CHOICES = [u'adminsecondaryemail', u'language', u'name', u'phone', u'address'] - -ADDRESS_FIELDS_ARGUMENT_MAP = { - u'contact': u'contactName', u'contactname': u'contactName', - u'name': u'organizationName', u'organizationname': u'organizationName', - u'address1': u'addressLine1', u'addressline1': u'addressLine1', - u'address2': u'addressLine2', u'addressline2': u'addressLine2', - u'address3': u'addressLine3', u'addressline3': u'addressLine3', - u'locality': u'locality', - u'region': u'region', - u'postalcode': u'postalCode', - u'country': u'countryCode', u'countrycode': u'countryCode', - } - def doUpdateInstance(): adminObj = getAdminSettingsObject() - cd = buildGAPIObject(u'directory') - command = sys.argv[3].lower().replace(u'_', u'') + command = sys.argv[3].lower() i = 4 - if command in CUSTOMER_ARGUMENT_CHOICES: - body = {} - if command == u'adminsecondaryemail': - body[u'alternateEmail'] = sys.argv[i] - elif command == u'language': - body[u'language'] = sys.argv[i] - elif command == u'phone': - body[u'phoneNumber'] = sys.argv[i] - elif command == u'name': - body[u'postalAddress'] = {u'organizationName': sys.argv[i]} - elif command == u'address': - body[u'postalAddress'] = {} - while i < len(sys.argv): - myarg = sys.argv[i].lower().replace(u'_', u'') - if myarg in ADDRESS_FIELDS_ARGUMENT_MAP: - value = sys.argv[i+1] - if not value: - value = None - body[u'postalAddress'][ADDRESS_FIELDS_ARGUMENT_MAP[myarg]] = value - i += 2 - else: - print u'ERROR: unknown option for "gam update instance address ...": %s' % sys.argv[i] - sys.exit(2) - callGAPI(cd.customers(), u'update', customerKey=GC_Values[GC_CUSTOMER_ID], body=body) + if command == u'language': + language = sys.argv[i] + callGData(service=adminObj, function=u'UpdateDefaultLanguage', defaultLanguage=language) + elif command == u'name': + name = sys.argv[i] + callGData(service=adminObj, function=u'UpdateOrganizationName', organizationName=name) + elif command == u'admin_secondary_email': + admin_secondary_email = sys.argv[i] + callGData(service=adminObj, function=u'UpdateAdminSecondaryEmail', adminSecondaryEmail=admin_secondary_email) elif command == u'logo': logoFile = sys.argv[i] logoImage = readFile(logoFile) callGData(service=adminObj, function=u'UpdateDomainLogo', logoImage=logoImage) - elif command == u'ssosettings': + elif command == u'sso_settings': enableSSO = samlSignonUri = samlLogoutUri = changePasswordUri = ssoWhitelist = useDomainSpecificIssuer = None while i < len(sys.argv): if sys.argv[i].lower() == u'enabled': @@ -7179,7 +7162,7 @@ def doUpdateInstance(): samlSignonUri=samlSignonUri, samlLogoutUri=samlLogoutUri, changePasswordUri=changePasswordUri, ssoWhitelist=ssoWhitelist, useDomainSpecificIssuer=useDomainSpecificIssuer) - elif command == u'ssokey': + elif command == u'sso_key': keyFile = sys.argv[i] keyData = readFile(keyFile) callGData(service=adminObj, function=u'UpdateSSOKey', signingKey=keyData) @@ -7187,8 +7170,6 @@ def doUpdateInstance(): print u'ERROR: %s is not a valid argument for "gam update instance"' % command sys.exit(2) -ADDRESS_FIELDS = [u'contactName', u'organizationName', u'addressLine1', u'addressLine2', u'addressLine3', u'locality', u'region', u'postalCode', u'countryCode'] - def doGetInstanceInfo(): adm = buildGAPIObject(u'admin-settings') if len(sys.argv) > 4 and sys.argv[3].lower() == u'logo': @@ -7196,24 +7177,7 @@ def doGetInstanceInfo(): url = 'http://www.google.com/a/cpanel/%s/images/logo.gif' % (GC_Values[GC_DOMAIN]) geturl(url, target_file) sys.exit(0) - cd = buildGAPIObject(u'directory') - customer_info = callGAPI(cd.customers(), u'get', customerKey=GC_Values[GC_CUSTOMER_ID]) - customerId = customer_info[u'id'] - primaryDomain = customer_info[u'customerDomain'] - print u'Customer ID: %s' % customerId - print u'Primary Domain: %s' % primaryDomain - print u'Customer Creation Time: %s' % customer_info[u'customerCreationTime'] - verified = callGAPI(cd.domains(), u'get', fields=u'verified', customer=customerId, domainName=primaryDomain)[u'verified'] - print u'Primary Domain Verified: %s' % verified - print u'Default Language: %s' % customer_info[u'language'] - if u'postalAddress' in customer_info: - print u'Address:' - for field in ADDRESS_FIELDS: - if field in customer_info[u'postalAddress']: - print u' %s: %s' % (field, customer_info[u'postalAddress'][field]) - if u'phoneNumber' in customer_info: - print u'Phone: %s' % customer_info[u'phoneNumber'] - print u'Admin Secondary Email: %s' % customer_info[u'alternateEmail'] + doGetCustomerInfo() max_users = callGAPI(service=adm.maximumNumberOfUsers(), function=u'get', domainName=GC_Values[GC_DOMAIN]) print u'Maximum Users: %s' % max_users[u'entry'][u'apps$property'][0][u'value'] current_users = callGAPI(service=adm.currentNumberOfUsers(), function=u'get', domainName=GC_Values[GC_DOMAIN]) From d78ea8efebd191bd284e613837d04b2e53ee800e Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Mon, 4 Apr 2016 05:20:32 -0700 Subject: [PATCH 05/80] Drop name, admin_secondary_email from update instance --- src/gam.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/gam.py b/src/gam.py index 7e7bfedc..3a867ac0 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7112,12 +7112,6 @@ def doUpdateInstance(): if command == u'language': language = sys.argv[i] callGData(service=adminObj, function=u'UpdateDefaultLanguage', defaultLanguage=language) - elif command == u'name': - name = sys.argv[i] - callGData(service=adminObj, function=u'UpdateOrganizationName', organizationName=name) - elif command == u'admin_secondary_email': - admin_secondary_email = sys.argv[i] - callGData(service=adminObj, function=u'UpdateAdminSecondaryEmail', adminSecondaryEmail=admin_secondary_email) elif command == u'logo': logoFile = sys.argv[i] logoImage = readFile(logoFile) From fdfa830aa0d26b30748ea4b90971060780607d67 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Mon, 4 Apr 2016 06:19:55 -0700 Subject: [PATCH 06/80] Handle language with admin settings --- src/gam.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/gam.py b/src/gam.py index 3a867ac0..2edd4209 100755 --- a/src/gam.py +++ b/src/gam.py @@ -1502,6 +1502,7 @@ ADDRESS_FIELDS_ARGUMENT_MAP = { def doUpdateCustomer(): cd = buildGAPIObject(u'directory') + language = None body = {} i = 3 while i < len(sys.argv): @@ -1517,12 +1518,17 @@ def doUpdateCustomer(): body[u'phoneNumber'] = sys.argv[i+1] i += 2 elif myarg == u'language': - body[u'language'] = sys.argv[i+1] +# body[u'language'] = sys.argv[i+1] + language = sys.argv[i+1] i += 2 else: print u'ERROR: %s is not a valid argument for "gam update customer"' % myarg sys.exit(2) - callGAPI(service=cd.customers(), function=u'update', customerKey=GC_Values[GC_CUSTOMER_ID], body=body) + if body: + callGAPI(service=cd.customers(), function=u'update', customerKey=GC_Values[GC_CUSTOMER_ID], body=body) + if language: + adminObj = getAdminSettingsObject() + callGData(service=adminObj, function=u'UpdateDefaultLanguage', defaultLanguage=language) print u'Updated customer' def doDelDomain(): @@ -7109,10 +7115,7 @@ def doUpdateInstance(): adminObj = getAdminSettingsObject() command = sys.argv[3].lower() i = 4 - if command == u'language': - language = sys.argv[i] - callGData(service=adminObj, function=u'UpdateDefaultLanguage', defaultLanguage=language) - elif command == u'logo': + if command == u'logo': logoFile = sys.argv[i] logoImage = readFile(logoFile) callGData(service=adminObj, function=u'UpdateDomainLogo', logoImage=logoImage) From bfc734138d0b7cbd94687d4839c76bc4c453d89d Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 6 Apr 2016 07:52:53 -0700 Subject: [PATCH 07/80] Fix create alias target where target is a group --- src/gam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gam.py b/src/gam.py index 2edd4209..9789fbf3 100755 --- a/src/gam.py +++ b/src/gam.py @@ -5566,7 +5566,7 @@ def doCreateAlias(): callGAPI(service=cd.groups().aliases(), function=u'insert', groupKey=targetKey, body=body) elif target_type == u'target': try: - callGAPI(service=cd.users().aliases(), function=u'insert', throw_reasons=[u'invalid'], userKey=targetKey, body=body) + callGAPI(service=cd.users().aliases(), function=u'insert', throw_reasons=[u'invalid', u'badRequest'], userKey=targetKey, body=body) except googleapiclient.errors.HttpError: callGAPI(service=cd.groups().aliases(), function=u'insert', groupKey=targetKey, body=body) From 5fa2f3d955b5256d56616753b9bac01830e9207b Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 6 Apr 2016 13:54:07 -0700 Subject: [PATCH 08/80] Add membernames to gam print group-members --- src/gam.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/gam.py b/src/gam.py index 9789fbf3..4a2ceacd 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7876,12 +7876,16 @@ def doPrintAliases(): def doPrintGroupMembers(): cd = buildGAPIObject(u'directory') - todrive = all_groups = False + todrive = membernames = False + all_groups = [] i = 3 while i < len(sys.argv): if sys.argv[i].lower() == u'todrive': todrive = True i += 1 + elif sys.argv[i].lower() == u'membernames': + membernames = True + i += 1 elif sys.argv[i].lower() == u'group': all_groups = [{u'email': sys.argv[i+1].lower()}] i += 2 @@ -7889,6 +7893,8 @@ def doPrintGroupMembers(): print 'ERROR: %s is not a valid argument for "gam print group-members"' % sys.argv[i] sys.exit(2) member_attributes = [{u'group': u'group'},] + if membernames: + member_attributes[0][u'memberName'] = u'memberName' if not all_groups: all_groups = callGAPIpages(service=cd.groups(), function=u'list', items=u'groups', message_attribute=u'email', customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,groups(email)') @@ -7908,6 +7914,26 @@ def doPrintGroupMembers(): except KeyError: member_attributes[0][title] = title member_attr[title] = member[title] + if membernames: + if member[u'type'] == u'USER': + try: + mbinfo = callGAPI(cd.users(), u'get', + throw_reasons=[u'notFound', u'forbidden'], + userKey=member[u'id'], fields=u'name') + memberName = mbinfo[u'name'][u'fullName'] + except googleapiclient.errors.HttpError: + memberName = u'Unknown' + elif member[u'type'] == u'GROUP': + try: + mbinfo = callGAPI(cd.groups(), u'get', + throw_reasons=[u'notFound', u'forbidden'], + groupKey=member[u'id'], fields=u'name') + memberName = mbinfo[u'name'] + except googleapiclient.errors.HttpError: + memberName = u'Unknown' + else: + memberName = u'Unknown' + member_attr[u'memberName'] = memberName member_attributes.append(member_attr) i += 1 titles = member_attributes[0].keys() From 0d507855bd093d7d97a29ee0ec74aaf7edbad8ff Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 8 Apr 2016 05:37:39 -0700 Subject: [PATCH 09/80] Add fields argument to gam print group-members --- src/gam.py | 67 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/gam.py b/src/gam.py index 4a2ceacd..eda7f9a9 100755 --- a/src/gam.py +++ b/src/gam.py @@ -7874,45 +7874,74 @@ def doPrintAliases(): continue output_csv(alias_attributes, titles, u'Aliases', todrive) +MEMBERS_FIELD_NAMES = [u'group', u'id', u'email', u'role', u'type', u'name',] + def doPrintGroupMembers(): cd = buildGAPIObject(u'directory') - todrive = membernames = False + todrive = groupname = membernames = False + fieldsList = [] + titles = [] all_groups = [] i = 3 while i < len(sys.argv): if sys.argv[i].lower() == u'todrive': todrive = True i += 1 + elif sys.argv[i].lower() == u'fields': + fieldNameList = sys.argv[i+1].lower() + for field in fieldNameList.lower().replace(u',', u' ').split(): + if field in MEMBERS_FIELD_NAMES: + fieldsList.append(field) + titles.append(field) + else: + print u'ERROR: field name should be %s. Got %s' % (u','.join(MEMBERS_FIELD_NAMES), field) + sys.exit(2) + i += 2 elif sys.argv[i].lower() == u'membernames': membernames = True i += 1 elif sys.argv[i].lower() == u'group': - all_groups = [{u'email': sys.argv[i+1].lower()}] + group_email = sys.argv[i+1].lower() + if group_email.find(u'@') == -1: + group_email = u'%s@%s' % (group_email, GC_Values[GC_DOMAIN]) + all_groups = [{u'email': group_email}] i += 2 else: - print 'ERROR: %s is not a valid argument for "gam print group-members"' % sys.argv[i] + print u'ERROR: %s is not a valid argument for "gam print group-members"' % sys.argv[i] sys.exit(2) - member_attributes = [{u'group': u'group'},] - if membernames: - member_attributes[0][u'memberName'] = u'memberName' + member_attributes = [{}] + if not fieldsList: + for field in [u'id', u'role', u'group', u'email', u'type']: + fieldsList.append(field) + titles.append(field) + member_attributes[0][field] = field + if membernames: + titles.append(u'name') + member_attributes[0][u'name'] = u'name' + else: + for field in fieldsList: + member_attributes[0][field] = field + if u'name'in fieldsList: + membernames = True + fieldsList.remove(u'name') + if u'group' in fieldsList: + groupname = True + fieldsList.remove(u'group') if not all_groups: all_groups = callGAPIpages(service=cd.groups(), function=u'list', items=u'groups', message_attribute=u'email', customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,groups(email)') - total_groups = len(all_groups) - i = 1 + i = 0 + count = len(all_groups) for group in all_groups: + i += 1 group_email = group[u'email'] - sys.stderr.write(u'Getting members for %s (%s/%s)\n' % (group_email, i, total_groups)) + sys.stderr.write(u'Getting members for %s (%s/%s)\n' % (group_email, i, count)) group_members = callGAPIpages(service=cd.members(), function=u'list', items=u'members', message_attribute=u'email', groupKey=group_email) for member in group_members: - member_attr = {u'group': group_email} - for title in member: - if title in [u'kind', u'etag']: - continue - try: - member_attributes[0][title] - except KeyError: - member_attributes[0][title] = title + member_attr = {} + if groupname: + member_attr[u'group'] = group_email + for title in fieldsList: member_attr[title] = member[title] if membernames: if member[u'type'] == u'USER': @@ -7933,10 +7962,8 @@ def doPrintGroupMembers(): memberName = u'Unknown' else: memberName = u'Unknown' - member_attr[u'memberName'] = memberName + member_attr[u'name'] = memberName member_attributes.append(member_attr) - i += 1 - titles = member_attributes[0].keys() output_csv(member_attributes, titles, u'Group Members', todrive) def doPrintMobileDevices(): From 45728fbbda8fdc75b6a5baa95642a4c5cacc226d Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 8 Apr 2016 10:57:39 -0700 Subject: [PATCH 10/80] Set character set based on OS --- src/gam.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gam.py b/src/gam.py index eda7f9a9..f5531ee6 100755 --- a/src/gam.py +++ b/src/gam.py @@ -68,6 +68,7 @@ ERROR = u'ERROR' ERROR_PREFIX = ERROR+u': ' WARNING = u'WARNING' WARNING_PREFIX = WARNING+u': ' +DEFAULT_CHARSET = [u'mbcs', u'utf-8'][os.name != u'nt'] FN_CLIENT_SECRETS_JSON = u'client_secrets.json' FN_EXTRA_ARGS_TXT = u'extra-args.txt' FN_LAST_UPDATE_CHECK_TXT = u'lastupdatecheck.txt' @@ -110,7 +111,7 @@ GM_Globals = { GM_SYSEXITRC: 0, GM_GAM_PATH: os.path.dirname(os.path.realpath(__file__)) if not getattr(sys, u'frozen', False) else os.path.dirname(sys.executable), GM_WINDOWS: os.name == u'nt', - GM_SYS_ENCODING: sys.getfilesystemencoding() if os.name == u'nt' else u'utf-8', + GM_SYS_ENCODING: DEFAULT_CHARSET, GM_BATCH_QUEUE: None, GM_EXTRA_ARGS_DICT: {u'prettyPrint': False}, GM_OAUTH2SERVICE_JSON_DATA: None, @@ -181,7 +182,7 @@ GC_Defaults = { GC_ACTIVITY_MAX_RESULTS: 100, GC_AUTO_BATCH_MIN: 0, GC_CACHE_DIR: u'', - GC_CHARSET: u'utf-8', + GC_CHARSET: DEFAULT_CHARSET, GC_CLIENT_SECRETS_JSON: FN_CLIENT_SECRETS_JSON, GC_CONFIG_DIR: u'', GC_CUSTOMER_ID: u'', From 457feac4ac6c299d66df15facec49cffd7e8aca4 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 13 Apr 2016 11:50:40 -0700 Subject: [PATCH 11/80] Cosmetic change --- src/gam.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/gam.py b/src/gam.py index f5531ee6..96064669 100755 --- a/src/gam.py +++ b/src/gam.py @@ -1558,10 +1558,7 @@ def doPrintDomains(): sys.exit(2) for domain in domains[u'domains']: domain_attributes = {} - if domain[u'isPrimary'] == True: - domain[u'type'] = u'primary' - else: - domain[u'type'] = u'secondary' + domain[u'type'] = [u'secondary', u'primary'][domain[u'isPrimary']] for attr in domain: if attr in [u'kind', u'domainAliases', u'etag', u'etags', u'isPrimary']: continue From 6c3a0e2b71144545f3ff2d4f21db78d599a797dd Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 15 Apr 2016 15:06:34 -0700 Subject: [PATCH 12/80] Handle info user/group arguments better in whatis --- src/gam.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/gam.py b/src/gam.py index 96064669..2fa2a844 100755 --- a/src/gam.py +++ b/src/gam.py @@ -6414,30 +6414,33 @@ def doGetUserInfo(user_email=None): projection = u'full' customFieldMask = viewType = None while i < len(sys.argv): - if sys.argv[i].lower() == u'noaliases': + myarg = sys.argv[i].lower() + if myarg == u'noaliases': getAliases = False i += 1 - elif sys.argv[i].lower() == u'nogroups': + elif myarg == u'nogroups': getGroups = False i += 1 - elif sys.argv[i].lower() in [u'nolicenses', u'nolicences']: + elif myarg in [u'nolicenses', u'nolicences']: getLicenses = False i += 1 - elif sys.argv[i].lower() == u'noschemas': + elif myarg == u'noschemas': getSchemas = False projection = u'basic' i += 1 - elif sys.argv[i].lower() == u'schemas': + elif myarg == u'schemas': getSchemas = True projection = u'custom' customFieldMask = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'userview': + elif myarg == u'userview': viewType = u'domain_public' getGroups = getLicenses = False i += 1 + elif myarg in [u'nousers', u'groups']: + i += 1 else: - print u'ERROR: %s is not a valid argument for "gam info user"' % sys.argv[i] + print u'ERROR: %s is not a valid argument for "gam info user"' % myarg sys.exit(2) user = callGAPI(service=cd.users(), function=u'get', userKey=user_email, projection=projection, customFieldMask=customFieldMask, viewType=viewType) print u'User: %s' % user[u'primaryEmail'] @@ -6591,17 +6594,22 @@ def doGetGroupInfo(group_name=None): else: i = 3 while i < len(sys.argv): - if sys.argv[i].lower() == u'nousers': + myarg = sys.argv[i].lower() + if myarg == u'nousers': getUsers = False i += 1 - elif sys.argv[i].lower() == u'noaliases': + elif myarg == u'noaliases': getAliases = False i += 1 - elif sys.argv[i].lower() == u'groups': + elif myarg == u'groups': getGroups = True i += 1 + elif myarg in [u'nogroups', u'nolicenses', u'nolicences', u'noschemas', u'schemas', u'userview']: + i += 1 + if myarg == u'schemas': + i += 1 else: - print u'ERROR: %s is not a valid argument for "gam info group"' % sys.argv[i] + print u'ERROR: %s is not a valid argument for "gam info group"' % myarg sys.exit(2) if group_name[:4].lower() == u'uid:': group_name = group_name[4:] From 3ada129e7f17c745f1856947d49c92c4fd1e9a57 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Tue, 19 Apr 2016 12:30:42 -0700 Subject: [PATCH 13/80] Add update drive file changes from master pull 212 --- src/gam.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/gam.py b/src/gam.py index 2fa2a844..5b8266a5 100755 --- a/src/gam.py +++ b/src/gam.py @@ -3562,7 +3562,7 @@ def deleteEmptyDriveFolders(users): print convertUTF8(u' not deleting folder %s because it contains at least 1 item (%s)' % (folder[u'title'], children[u'items'][0][u'id'])) def doUpdateDriveFile(users): - convert = ocr = ocrLanguage = parent_query = local_filepath = media_body = fileIds = drivefilename = None + convert = ocr = ocrLanguage = parent_query = local_filepath = media_body = fileIds = query = drivefilename = None operation = u'update' i = 5 body = {} @@ -3582,6 +3582,9 @@ def doUpdateDriveFile(users): elif sys.argv[i].lower() == u'id': fileIds = [sys.argv[i+1],] i += 2 + elif sys.argv[i].lower() == u'query': + query = sys.argv[i+1] + i += 2 elif sys.argv[i].lower() == u'drivefilename': drivefilename = sys.argv[i+1] i += 2 @@ -3597,8 +3600,8 @@ def doUpdateDriveFile(users): elif sys.argv[i].lower() in [u'ocrlanguage',]: ocrLanguage = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() in [u'restrict', 'restricted']: - if 'labels' not in body: + elif sys.argv[i].lower() in [u'restrict', u'restricted']: + if u'labels' not in body: body[u'labels'] = dict() if sys.argv[i+1].lower() in true_values: body[u'labels'][u'restricted'] = True @@ -3685,11 +3688,11 @@ def doUpdateDriveFile(users): else: print u'ERROR: %s is not a valid argument for "gam update drivefile"' % sys.argv[i] sys.exit(2) - if not fileIds and not drivefilename: - print u'ERROR: you need to specify either id or query in order to determine the file(s) to update' + if not fileIds and not drivefilename and not query: + print u'ERROR: you need to specify either id, query or drivefilename in order to determine the file(s) to update' sys.exit(2) - elif fileIds and drivefilename: - print u'ERROR: you cannot specify both an id and a query.' + elif (fileIds and drivefilename) or (fileIds and query) or (drivefilename and query): + print u'ERROR: you cannot specify multiple file identifiers. Choose one of id, drivefilename, query.' sys.exit(2) for user in users: drive = buildGAPIServiceObject(u'drive', user) @@ -3699,7 +3702,9 @@ def doUpdateDriveFile(users): body[u'parents'] = list() for a_parent in more_parents: body[u'parents'].append({u'id': a_parent}) - if drivefilename: + if query: + fileIds = doDriveSearch(drive, query=query) + elif drivefilename: fileIds = doDriveSearch(drive, query=u'"me" in owners and title = "%s"' % drivefilename) if local_filepath: media_body = googleapiclient.http.MediaFileUpload(local_filepath, mimetype=mimetype, resumable=True) From f2887abb49c65774aa2e459838663309435dddad Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 20 Apr 2016 09:13:59 -0700 Subject: [PATCH 14/80] Fix error in app2appID --- src/gam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gam.py b/src/gam.py index 5b8266a5..adf3b22c 100755 --- a/src/gam.py +++ b/src/gam.py @@ -1787,7 +1787,7 @@ SERVICE_NAME_CHOICES_MAP = { def app2appID(dt, app): serviceName = app.lower() if serviceName in SERVICE_NAME_CHOICES_MAP: - return (SERVICE_NAME_CHOICES_MAP[serviceName], SERVICE_NAME_TO_ID_MAP[SERVICE_NAME_CHOICES_MAP[serviceName]]) + return SERVICE_NAME_TO_ID_MAP[SERVICE_NAME_CHOICES_MAP[serviceName]] online_services = callGAPIpages(service=dt.applications(), function=u'list', items=u'applications', customerId=GC_Values[GC_CUSTOMER_ID]) for online_service in online_services: if serviceName == online_service[u'name'].lower(): From 4ea63c31675926e6c4f28dca181fc2db6ad3487d Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 20 Apr 2016 12:57:21 -0700 Subject: [PATCH 15/80] Add skus to gam info user --- src/gam.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/gam.py b/src/gam.py index adf3b22c..6c83217b 100755 --- a/src/gam.py +++ b/src/gam.py @@ -6418,6 +6418,8 @@ def doGetUserInfo(user_email=None): getSchemas = getAliases = getGroups = getLicenses = True projection = u'full' customFieldMask = viewType = None + skus = [u'Google-Apps-For-Business', u'Google-Apps-Unlimited', u'Google-Apps-For-Postini', + u'Google-Apps-Lite', u'Google-Vault', u'Google-Vault-Former-Employee'] while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == u'noaliases': @@ -6429,6 +6431,9 @@ def doGetUserInfo(user_email=None): elif myarg in [u'nolicenses', u'nolicences']: getLicenses = False i += 1 + elif myarg in [u'sku', u'skus']: + skus = sys.argv[i+1].replace(u',', u' ').split() + i += 2 elif myarg == u'noschemas': getSchemas = False projection = u'basic' @@ -6578,12 +6583,11 @@ def doGetUserInfo(user_email=None): print u' %s <%s>' % (group[u'name'], group[u'email']) if getLicenses: print u'Licenses:' - lic = buildGAPIObject(api='licensing') - for sku in [u'Google-Apps-For-Business', u'Google-Apps-Unlimited', u'Google-Apps-For-Postini', - u'Google-Apps-Lite', u'Google-Vault', u'Google-Vault-Former-Employee']: + lic = buildGAPIObject(u'licensing') + for sku in skus: productId, skuId = getProductAndSKU(sku) try: - result = callGAPI(service=lic.licenseAssignments(), function=u'get', throw_reasons=['notFound'], userId=user_email, productId=productId, skuId=skuId) + result = callGAPI(service=lic.licenseAssignments(), function=u'get', throw_reasons=[u'notFound', u'invalid', u'forbidden'], userId=user_email, productId=productId, skuId=skuId) except googleapiclient.errors.HttpError: continue print u' %s' % result[u'skuId'] @@ -8151,10 +8155,10 @@ def doPrintLicenses(return_list=False, skus=None): todrive = True i += 1 elif sys.argv[i].lower() in [u'products', u'product']: - products = sys.argv[i+1].split(',') + products = sys.argv[i+1].replace(u',', u' ').split() i += 2 elif sys.argv[i].lower() in [u'sku', u'skus']: - skus = sys.argv[i+1].split(',') + skus = sys.argv[i+1].replace(u',', u' ').split() i += 2 else: print u'ERROR: %s is not a valid argument for "gam print licenses"' % sys.argv[i] @@ -8167,7 +8171,7 @@ def doPrintLicenses(return_list=False, skus=None): licenses += callGAPIpages(service=lic.licenseAssignments(), function=u'listForProductAndSku', throw_reasons=[u'invalid', u'forbidden'], page_message=page_message, customerId=GC_Values[GC_DOMAIN], productId=product, skuId=sku, fields=u'items(productId,skuId,userId),nextPageToken') except googleapiclient.errors.HttpError: - licenses += [] + pass else: for productId in products: page_message = u'Got %%%%total_items%%%% Licenses for %s...\n' % productId @@ -8175,7 +8179,7 @@ def doPrintLicenses(return_list=False, skus=None): licenses += callGAPIpages(service=lic.licenseAssignments(), function=u'listForProduct', throw_reasons=[u'invalid', u'forbidden'], page_message=page_message, customerId=GC_Values[GC_DOMAIN], productId=productId, fields=u'items(productId,skuId,userId),nextPageToken') except googleapiclient.errors.HttpError: - licenses = +[] + pass for u_license in licenses: a_license = dict() for title in u_license: From 1f2ffcf97ac13c33f036dbb5fcca676b1f2ea90f Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 20 Apr 2016 16:05:06 -0700 Subject: [PATCH 16/80] Handle UTF8 user name in show drivefileacl --- src/gam.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gam.py b/src/gam.py index 6c83217b..0dd73339 100755 --- a/src/gam.py +++ b/src/gam.py @@ -3238,7 +3238,7 @@ def showDriveFileACL(users): feed = callGAPI(service=drive.permissions(), function=u'list', fileId=fileId) for permission in feed[u'items']: try: - print permission[u'name'] + print convertUTF8(permission[u'name']) except KeyError: pass for key in permission: From d850f5575b60dd7a76979904ef7db46d3145a201 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Tue, 26 Apr 2016 10:27:34 -0700 Subject: [PATCH 17/80] Add offset, limit arguments to print printjobs and printjob fetch gam print printjobs ... offset limit gam printjob fetch ... offset limit --- src/gam.py | 66 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/src/gam.py b/src/gam.py index 0dd73339..d8c7bfab 100755 --- a/src/gam.py +++ b/src/gam.py @@ -2120,15 +2120,17 @@ def doPrintPrintJobs(): sortorder = None descending = False query = None - i = 3 age = None older_or_newer = None + offset = limit = None + i = 3 while i < len(sys.argv): - if sys.argv[i].lower() == u'todrive': + myarg = sys.argv[i].lower().replace(u'_', u'') + if myarg == u'todrive': todrive = True i += 1 - elif sys.argv[i].lower().replace(u'_', u'') in [u'olderthan', u'newerthan']: - if sys.argv[i].lower().replace(u'_', u'') == u'olderthan': + elif myarg in [u'olderthan', u'newerthan']: + if myarg == u'olderthan': older_or_newer = u'older' else: older_or_newer = u'newer' @@ -2147,37 +2149,45 @@ def doPrintPrintJobs(): print u'ERROR: expected m (minutes), h (hours) or d (days), got %s' % age_unit sys.exit(2) i += 2 - elif sys.argv[i].lower() == u'query': + elif myarg == u'query': query = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'status': + elif myarg == u'status': status = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'ascending': + elif myarg == u'ascending': descending = False i += 1 - elif sys.argv[i].lower() == u'descending': + elif myarg == u'descending': descending = True i += 1 - elif sys.argv[i].lower() == u'orderby': + elif myarg == u'orderby': sortorder = sys.argv[i+1].lower().replace(u'_', u'') if sortorder not in PRINTJOB_ASCENDINGORDER_MAP: - print u'ERROR: orderby must be one of %s. Got %s' % (','.join(PRINTJOB_ASCENDINGORDER_MAP), sortorder) + print u'ERROR: orderby must be one of %s. Got %s' % (u','.join(PRINTJOB_ASCENDINGORDER_MAP), sortorder) sys.exit(2) sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder] i += 2 - elif sys.argv[i].lower() in [u'printer', u'printerid']: + elif myarg in [u'printer', u'printerid']: printerid = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() in [u'owner', u'user']: + elif myarg in [u'owner', u'user']: owner = sys.argv[i+1] i += 2 + elif myarg == u'offset': + offset = max(0, int(sys.argv[i+1])) + i += 2 + elif myarg == u'limit': + limit = max(1, int(sys.argv[i+1])) + i += 2 else: print u'ERROR: %s is not a valid argument for "gam print printjobs"' % sys.argv[i] sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] - jobs = callGAPI(service=cp.jobs(), function=u'list', q=query, status=status, sortorder=sortorder, printerid=printerid, owner=owner) + jobs = callGAPI(service=cp.jobs(), function=u'list', + printerid=printerid, q=query, status=status, sortorder=sortorder, + owner=owner, offset=offset, limit=limit) checkCloudPrintResult(jobs) for job in jobs[u'jobs']: createTime = int(job[u'createTime'])/1000 @@ -2551,10 +2561,12 @@ def doPrintJobFetch(): query = None age = None older_or_newer = None + offset = limit = None i = 4 while i < len(sys.argv): - if sys.argv[i].lower().replace(u'_', u'') in [u'olderthan', u'newerthan']: - if sys.argv[i].lower().replace(u'_', u'') == u'olderthan': + myarg = sys.argv[i].lower().replace(u'_', u'') + if myarg in [u'olderthan', u'newerthan']: + if myarg == u'olderthan': older_or_newer = u'older' else: older_or_newer = u'newer' @@ -2573,34 +2585,42 @@ def doPrintJobFetch(): print u'ERROR: expected m (minutes), h (hours) or d (days), got %s' % age_unit sys.exit(2) i += 2 - elif sys.argv[i].lower() == u'query': + elif myarg == u'query': query = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'status': + elif myarg == u'status': status = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'ascending': + elif myarg == u'ascending': descending = False i += 1 - elif sys.argv[i].lower() == u'descending': + elif myarg == u'descending': descending = True i += 1 - elif sys.argv[i].lower() == u'orderby': + elif myarg == u'orderby': sortorder = sys.argv[i+1].lower().replace(u'_', u'') if sortorder not in PRINTJOB_ASCENDINGORDER_MAP: - print 'ERROR: orderby must be one of %s. Got %s' % (','.join(PRINTJOB_ASCENDINGORDER_MAP), sortorder) + print u'ERROR: orderby must be one of %s. Got %s' % (u','.join(PRINTJOB_ASCENDINGORDER_MAP), sortorder) sys.exit(2) sortorder = PRINTJOB_ASCENDINGORDER_MAP[sortorder] i += 2 - elif sys.argv[i].lower() in [u'owner', u'user']: + elif myarg in [u'owner', u'user']: owner = sys.argv[i+1] i += 2 + elif myarg == u'offset': + offset = max(0, int(sys.argv[i+1])) + i += 2 + elif myarg == u'limit': + limit = max(1, int(sys.argv[i+1])) + i += 2 else: print u'ERROR: %s is not a valid argument for "gam printjobs fetch"' % sys.argv[i] sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] - result = callGAPI(service=cp.jobs(), function=u'list', q=query, status=status, sortorder=sortorder, printerid=printerid, owner=owner) + result = callGAPI(service=cp.jobs(), function=u'list', + printerid=printerid, q=query, status=status, sortorder=sortorder, + owner=owner, offset=offset, limit=limit) if u'errorCode' in result and result[u'errorCode'] == 413: print u'No print jobs.' sys.exit(0) From 49d184512914dae95daec328ac43fa4c197f9011 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Tue, 26 Apr 2016 14:51:45 -0700 Subject: [PATCH 18/80] Update offset/limits processing for print jobs --- src/gam.py | 126 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/src/gam.py b/src/gam.py index d8c7bfab..8f45fd02 100755 --- a/src/gam.py +++ b/src/gam.py @@ -2122,7 +2122,8 @@ def doPrintPrintJobs(): query = None age = None older_or_newer = None - offset = limit = None + offset = 0 + jobLimit = 25 i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2178,33 +2179,43 @@ def doPrintPrintJobs(): offset = max(0, int(sys.argv[i+1])) i += 2 elif myarg == u'limit': - limit = max(1, int(sys.argv[i+1])) + jobLimit = max(0, int(sys.argv[i+1])) i += 2 else: print u'ERROR: %s is not a valid argument for "gam print printjobs"' % sys.argv[i] sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] - jobs = callGAPI(service=cp.jobs(), function=u'list', - printerid=printerid, q=query, status=status, sortorder=sortorder, - owner=owner, offset=offset, limit=limit) - checkCloudPrintResult(jobs) - for job in jobs[u'jobs']: - createTime = int(job[u'createTime'])/1000 - if older_or_newer: - if older_or_newer == u'older' and createTime > age: - continue - elif older_or_newer == u'newer' and createTime < age: - continue - updateTime = int(job[u'updateTime'])/1000 - job[u'createTime'] = datetime.datetime.fromtimestamp(createTime).strftime(u'%Y-%m-%d %H:%M:%S') - job[u'updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime(u'%Y-%m-%d %H:%M:%S') - job[u'tags'] = u' '.join(job[u'tags']) - job_attributes.append(flatten_json(job)) - for item in job_attributes[-1]: - if item not in titles: - titles.append(item) - job_attributes[0][item] = item + jobCount = 0 + while True: + limit = 25 if jobLimit == 0 else jobLimit-jobCount + if limit == 0: + break + result = callGAPI(cp.jobs(), u'list', + printerid=printerid, q=query, status=status, sortorder=sortorder, + owner=owner, offset=offset, limit=limit) + checkCloudPrintResult(result) + newJobs = result[u'range'][u'jobsCount'] + if newJobs == 0: + break + jobCount += newJobs + offset += newJobs + for job in result[u'jobs']: + createTime = int(job[u'createTime'])/1000 + if older_or_newer: + if older_or_newer == u'older' and createTime > age: + continue + elif older_or_newer == u'newer' and createTime < age: + continue + updateTime = int(job[u'updateTime'])/1000 + job[u'createTime'] = datetime.datetime.fromtimestamp(createTime).strftime(u'%Y-%m-%d %H:%M:%S') + job[u'updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime(u'%Y-%m-%d %H:%M:%S') + job[u'tags'] = u' '.join(job[u'tags']) + job_attributes.append(flatten_json(job)) + for item in job_attributes[-1]: + if item not in titles: + titles.append(item) + job_attributes[0][item] = item output_csv(job_attributes, titles, u'Print Jobs', todrive) def doPrintPrinters(): @@ -2236,7 +2247,7 @@ def doPrintPrinters(): else: print u'ERROR: %s is not a valid argument for "gam print printers"' % sys.argv[i] sys.exit(2) - printers = callGAPI(service=cp.printers(), function=u'list', q=query, type=printer_type, connection_status=connection_status, extra_fields=extra_fields) + printers = callGAPI(cp.printers(), u'list', q=query, type=printer_type, connection_status=connection_status, extra_fields=extra_fields) checkCloudPrintResult(printers) for printer in printers[u'printers']: createTime = int(printer[u'createTime'])/1000 @@ -2561,7 +2572,8 @@ def doPrintJobFetch(): query = None age = None older_or_newer = None - offset = limit = None + offset = 0 + jobLimit = 25 i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2611,42 +2623,50 @@ def doPrintJobFetch(): offset = max(0, int(sys.argv[i+1])) i += 2 elif myarg == u'limit': - limit = max(1, int(sys.argv[i+1])) + jobLimit = max(0, int(sys.argv[i+1])) i += 2 else: print u'ERROR: %s is not a valid argument for "gam printjobs fetch"' % sys.argv[i] sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] - result = callGAPI(service=cp.jobs(), function=u'list', - printerid=printerid, q=query, status=status, sortorder=sortorder, - owner=owner, offset=offset, limit=limit) - if u'errorCode' in result and result[u'errorCode'] == 413: - print u'No print jobs.' - sys.exit(0) - checkCloudPrintResult(result) valid_chars = u'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' - ssd = '''{ - "state": {"type": "DONE"} -}''' - for job in result[u'jobs']: - createTime = int(job[u'createTime'])/1000 - if older_or_newer: - if older_or_newer == u'older' and createTime > age: - continue - elif older_or_newer == u'newer' and createTime < age: - continue - fileUrl = job[u'fileUrl'] - jobid = job[u'id'] - fileName = job[u'title'] - fileName = u''.join(c if c in valid_chars else u'_' for c in fileName) - fileName = u'%s-%s' % (fileName, jobid) - _, content = cp._http.request(uri=fileUrl, method='GET') - if writeFile(fileName, content, continueOnError=True): -# ticket = callGAPI(service=cp.jobs(), function=u'getticket', jobid=jobid, use_cjt=True) - result = callGAPI(service=cp.jobs(), function=u'update', jobid=jobid, semantic_state_diff=ssd) - checkCloudPrintResult(result) - print u'Printed job %s to %s' % (jobid, fileName) + ssd = u'{"state": {"type": "DONE"}}' + jobCount = 0 + while True: + limit = 25 if jobLimit == 0 else jobLimit-jobCount + if limit == 0: + break + result = callGAPI(cp.jobs(), u'list', + printerid=printerid, q=query, status=status, sortorder=sortorder, + owner=owner, offset=offset, limit=limit) + if u'errorCode' in result and result[u'errorCode'] == 413: + print u'No print jobs.' + sys.exit(0) + checkCloudPrintResult(result) + newJobs = result[u'range'][u'jobsCount'] + if newJobs == 0: + break + jobCount += newJobs + offset += newJobs + for job in result[u'jobs']: + createTime = int(job[u'createTime'])/1000 + if older_or_newer: + if older_or_newer == u'older' and createTime > age: + continue + elif older_or_newer == u'newer' and createTime < age: + continue + fileUrl = job[u'fileUrl'] + jobid = job[u'id'] + fileName = job[u'title'] + fileName = u''.join(c if c in valid_chars else u'_' for c in fileName) + fileName = u'%s-%s' % (fileName, jobid) + _, content = cp._http.request(uri=fileUrl, method='GET') + if writeFile(fileName, content, continueOnError=True): + # ticket = callGAPI(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True) + result = callGAPI(cp.jobs(), u'update', jobid=jobid, semantic_state_diff=ssd) + checkCloudPrintResult(result) + print u'Printed job %s to %s' % (jobid, fileName) def doDelPrinter(): cp = buildGAPIObject(u'cloudprint') From 289fda94df00c7835cfa096247ec8189e521f737 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 27 Apr 2016 06:58:54 -0700 Subject: [PATCH 19/80] Add maxresults argument to gam print printjobs/gam printjob fetch --- src/gam.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/gam.py b/src/gam.py index 8f45fd02..2977d40f 100755 --- a/src/gam.py +++ b/src/gam.py @@ -2109,6 +2109,9 @@ PRINTJOB_DESCENDINGORDER_MAP = { u'TITLE': u'TITLE_DESC', } +PRINTJOBS_DEFAULT_JOB_LIMIT = 25 +PRINTJOBS_DEFAULT_MAX_RESULTS = 100 + def doPrintPrintJobs(): cp = buildGAPIObject(u'cloudprint') job_attributes = [{}] @@ -2123,7 +2126,8 @@ def doPrintPrintJobs(): age = None older_or_newer = None offset = 0 - jobLimit = 25 + jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT + maxResults = PRINTJOBS_DEFAULT_MAX_RESULTS i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2181,6 +2185,9 @@ def doPrintPrintJobs(): elif myarg == u'limit': jobLimit = max(0, int(sys.argv[i+1])) i += 2 + elif myarg == u'maxresults': + maxResults = min(1000, max(1, int(sys.argv[i+1]))) + i += 2 else: print u'ERROR: %s is not a valid argument for "gam print printjobs"' % sys.argv[i] sys.exit(2) @@ -2188,9 +2195,12 @@ def doPrintPrintJobs(): sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] jobCount = 0 while True: - limit = 25 if jobLimit == 0 else jobLimit-jobCount - if limit == 0: - break + if jobLimit == 0: + limit = maxResults + else: + limit = min(maxResults, jobLimit-jobCount) + if limit == 0: + break result = callGAPI(cp.jobs(), u'list', printerid=printerid, q=query, status=status, sortorder=sortorder, owner=owner, offset=offset, limit=limit) @@ -2573,7 +2583,8 @@ def doPrintJobFetch(): age = None older_or_newer = None offset = 0 - jobLimit = 25 + jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT + maxResults = PRINTJOBS_DEFAULT_MAX_RESULTS i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2625,6 +2636,9 @@ def doPrintJobFetch(): elif myarg == u'limit': jobLimit = max(0, int(sys.argv[i+1])) i += 2 + elif myarg == u'maxresults': + maxResults = min(1000, max(1, int(sys.argv[i+1]))) + i += 2 else: print u'ERROR: %s is not a valid argument for "gam printjobs fetch"' % sys.argv[i] sys.exit(2) @@ -2634,9 +2648,12 @@ def doPrintJobFetch(): ssd = u'{"state": {"type": "DONE"}}' jobCount = 0 while True: - limit = 25 if jobLimit == 0 else jobLimit-jobCount - if limit == 0: - break + if jobLimit == 0: + limit = maxResults + else: + limit = min(maxResults, jobLimit-jobCount) + if limit == 0: + break result = callGAPI(cp.jobs(), u'list', printerid=printerid, q=query, status=status, sortorder=sortorder, owner=owner, offset=offset, limit=limit) @@ -2663,7 +2680,7 @@ def doPrintJobFetch(): fileName = u'%s-%s' % (fileName, jobid) _, content = cp._http.request(uri=fileUrl, method='GET') if writeFile(fileName, content, continueOnError=True): - # ticket = callGAPI(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True) +# ticket = callGAPI(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True) result = callGAPI(cp.jobs(), u'update', jobid=jobid, semantic_state_diff=ssd) checkCloudPrintResult(result) print u'Printed job %s to %s' % (jobid, fileName) From ee00a41ff99f8e10d57ae0943c3b99da9a5bd292 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Wed, 27 Apr 2016 10:27:33 -0700 Subject: [PATCH 20/80] Eliminate offset/maxresults arguments for gam print printjobs/gam printjob fetch --- src/gam.py | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/gam.py b/src/gam.py index 2977d40f..32326bc5 100755 --- a/src/gam.py +++ b/src/gam.py @@ -2125,9 +2125,7 @@ def doPrintPrintJobs(): query = None age = None older_or_newer = None - offset = 0 jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT - maxResults = PRINTJOBS_DEFAULT_MAX_RESULTS i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2179,26 +2177,20 @@ def doPrintPrintJobs(): elif myarg in [u'owner', u'user']: owner = sys.argv[i+1] i += 2 - elif myarg == u'offset': - offset = max(0, int(sys.argv[i+1])) - i += 2 elif myarg == u'limit': jobLimit = max(0, int(sys.argv[i+1])) i += 2 - elif myarg == u'maxresults': - maxResults = min(1000, max(1, int(sys.argv[i+1]))) - i += 2 else: print u'ERROR: %s is not a valid argument for "gam print printjobs"' % sys.argv[i] sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] - jobCount = 0 + jobCount = offset = 0 while True: if jobLimit == 0: - limit = maxResults + limit = PRINTJOBS_DEFAULT_MAX_RESULTS else: - limit = min(maxResults, jobLimit-jobCount) + limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit-jobCount) if limit == 0: break result = callGAPI(cp.jobs(), u'list', @@ -2582,9 +2574,7 @@ def doPrintJobFetch(): query = None age = None older_or_newer = None - offset = 0 jobLimit = PRINTJOBS_DEFAULT_JOB_LIMIT - maxResults = PRINTJOBS_DEFAULT_MAX_RESULTS i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace(u'_', u'') @@ -2630,15 +2620,9 @@ def doPrintJobFetch(): elif myarg in [u'owner', u'user']: owner = sys.argv[i+1] i += 2 - elif myarg == u'offset': - offset = max(0, int(sys.argv[i+1])) - i += 2 elif myarg == u'limit': jobLimit = max(0, int(sys.argv[i+1])) i += 2 - elif myarg == u'maxresults': - maxResults = min(1000, max(1, int(sys.argv[i+1]))) - i += 2 else: print u'ERROR: %s is not a valid argument for "gam printjobs fetch"' % sys.argv[i] sys.exit(2) @@ -2646,12 +2630,12 @@ def doPrintJobFetch(): sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] valid_chars = u'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ssd = u'{"state": {"type": "DONE"}}' - jobCount = 0 + jobCount = offset = 0 while True: if jobLimit == 0: - limit = maxResults + limit = PRINTJOBS_DEFAULT_MAX_RESULTS else: - limit = min(maxResults, jobLimit-jobCount) + limit = min(PRINTJOBS_DEFAULT_MAX_RESULTS, jobLimit-jobCount) if limit == 0: break result = callGAPI(cp.jobs(), u'list', From 52f4c049e14521496a08b9b346ed247a79b5f896 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Thu, 28 Apr 2016 07:20:27 -0700 Subject: [PATCH 21/80] Handle invalid printer ID in gam print printjobs/gam printjob fetch --- src/gam.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/gam.py b/src/gam.py index 32326bc5..7461cc77 100755 --- a/src/gam.py +++ b/src/gam.py @@ -2185,6 +2185,10 @@ def doPrintPrintJobs(): sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] + if printerid: + result = callGAPI(cp.printers(), u'get', + printerid=printerid) + checkCloudPrintResult(result) jobCount = offset = 0 while True: if jobLimit == 0: @@ -2628,6 +2632,10 @@ def doPrintJobFetch(): sys.exit(2) if sortorder and descending: sortorder = PRINTJOB_DESCENDINGORDER_MAP[sortorder] + if printerid: + result = callGAPI(cp.printers(), u'get', + printerid=printerid) + checkCloudPrintResult(result) valid_chars = u'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ssd = u'{"state": {"type": "DONE"}}' jobCount = offset = 0 @@ -2641,9 +2649,6 @@ def doPrintJobFetch(): result = callGAPI(cp.jobs(), u'list', printerid=printerid, q=query, status=status, sortorder=sortorder, owner=owner, offset=offset, limit=limit) - if u'errorCode' in result and result[u'errorCode'] == 413: - print u'No print jobs.' - sys.exit(0) checkCloudPrintResult(result) newJobs = result[u'range'][u'jobsCount'] if newJobs == 0: @@ -2659,15 +2664,15 @@ def doPrintJobFetch(): continue fileUrl = job[u'fileUrl'] jobid = job[u'id'] - fileName = job[u'title'] - fileName = u''.join(c if c in valid_chars else u'_' for c in fileName) - fileName = u'%s-%s' % (fileName, jobid) + fileName = u'{0}-{1}'.format(u''.join(c if c in valid_chars else u'_' for c in job[u'title']), jobid) _, content = cp._http.request(uri=fileUrl, method='GET') if writeFile(fileName, content, continueOnError=True): # ticket = callGAPI(cp.jobs(), u'getticket', jobid=jobid, use_cjt=True) result = callGAPI(cp.jobs(), u'update', jobid=jobid, semantic_state_diff=ssd) checkCloudPrintResult(result) print u'Printed job %s to %s' % (jobid, fileName) + if jobCount == 0: + print u'No print jobs.' def doDelPrinter(): cp = buildGAPIObject(u'cloudprint') From ef7fcb2114f96d573a2442e0438ac8153a8c4884 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Thu, 28 Apr 2016 14:38:19 -0700 Subject: [PATCH 22/80] Add revision argument to gam get drivefile; add command gam show filerevisions --- src/gam.py | 92 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/src/gam.py b/src/gam.py index 7461cc77..a55a20f9 100755 --- a/src/gam.py +++ b/src/gam.py @@ -3876,24 +3876,28 @@ def createDriveFile(users): def downloadDriveFile(users): i = 5 - query = fileIds = None + query = fileIds = revision = None gdownload_format = u'openoffice' target_folder = GC_Values[GC_DRIVE_DIR] safe_filename_chars = "-_.() %s%s" % (string.ascii_letters, string.digits) while i < len(sys.argv): - if sys.argv[i].lower() == u'id': + myarg = sys.argv[i].lower().replace('_', '') + if myarg == u'id': fileIds = [sys.argv[i+1],] i += 2 - elif sys.argv[i].lower() == 'query': + elif myarg == u'query': query = sys.argv[i+1] i += 2 - elif sys.argv[i].lower() == u'format': + elif myarg == u'revision': + revision = sys.argv[i+1] + i += 2 + elif myarg == u'format': gdownload_format = sys.argv[i+1].lower() if gdownload_format not in [u'openoffice', u'ms', u'microsoft', u'micro$oft', u'pdf']: - print 'ERROR: format must be one of openoffice, microsoft or pdf. Got %s' % gdownload_format + print u'ERROR: format must be one of openoffice, microsoft or pdf. Got %s' % gdownload_format sys.exit(2) i += 2 - elif sys.argv[i].lower().replace('_', '') == u'targetfolder': + elif myarg == u'targetfolder': target_folder = sys.argv[i+1] if not os.path.isdir(target_folder): os.makedirs(target_folder) @@ -3901,7 +3905,7 @@ def downloadDriveFile(users): else: print u'ERROR: %s is not a valid argument for "gam get drivefile"' % sys.argv[i] sys.exit(2) - export_extensions = {u'application/pdf': '.pdf', + export_extensions = {u'application/pdf': u'.pdf', u'application/vnd.openxmlformats-officedocument.wordprocessingml.document': u'.docx', u'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': u'.xlsx', u'application/vnd.openxmlformats-officedocument.presentationml.presentation': u'.pptx', @@ -3927,10 +3931,10 @@ def downloadDriveFile(users): if query: fileIds = doDriveSearch(drive, query=query) else: - if fileIds[0][:8].lower() == 'https://' or fileIds[0][:7].lower() == 'http://': - fileIds[0] = fileIds[0][fileIds[0].find('/d/')+3:] - if fileIds[0].find('/') != -1: - fileIds[0] = fileIds[0][:fileIds[0].find('/')] + if fileIds[0][:8].lower() == u'https://' or fileIds[0][:7].lower() == u'http://': + fileIds[0] = fileIds[0][fileIds[0].find(u'/d/')+3:] + if fileIds[0].find(u'/') != -1: + fileIds[0] = fileIds[0][:fileIds[0].find(u'/')] if not fileIds: print u'No files to download for %s' % user i = 0 @@ -3965,13 +3969,13 @@ def downloadDriveFile(users): pass break else: - print convertUTF8(u'Skipping download of file {0}, Format {1} not available'.format(result[u'title'], ','.join(export_formats))) + print convertUTF8(u'Skipping download of file {0}, Format {1} not available'.format(result[u'title'], u','.join(export_formats))) continue else: print convertUTF8(u'Skipping download of file {0}, Format not downloadable') continue file_title = result[u'title'] - safe_file_title = ''.join(c for c in file_title if c in safe_filename_chars) + safe_file_title = u''.join(c for c in file_title if c in safe_filename_chars) filename = os.path.join(target_folder, safe_file_title) if extension and filename.lower()[:len(extension)] != extension: filename = u'%s%s' % (filename, extension) @@ -3986,38 +3990,50 @@ def downloadDriveFile(users): break filename = new_filename print convertUTF8(my_line % filename) + if revision: + download_url = u'{0}&revision={1}'.format(download_url, revision) _, content = drive._http.request(download_url) writeFile(filename, content, continueOnError=True) +def printDriveFileData(feed): + for setting in feed: + if setting in [u'kind', u'etag']: + continue + setting_type = str(type(feed[setting])) + if setting_type == u"": + print u'%s:' % setting + for settin in feed[setting]: + if settin in [u'kind', u'etag']: + continue + settin_type = str(type(settin)) + if settin_type == u"": + for setti in settin: + if setti in [u'kind', u'etag']: + continue + print convertUTF8(u' %s: %s' % (setti, settin[setti])) + print u'' + elif setting_type == u"": + print u'%s:' % setting + for settin in feed[setting]: + if settin in [u'kind', u'etag']: + continue + print convertUTF8(u' %s: %s' % (settin, feed[setting][settin])) + else: + print convertUTF8(u'%s: %s' % (setting, feed[setting])) + def showDriveFileInfo(users): for user in users: fileId = sys.argv[5] drive = buildGAPIServiceObject(u'drive', user) feed = callGAPI(service=drive.files(), function=u'get', fileId=fileId) - for setting in feed: - if setting == u'kind': - continue - setting_type = str(type(feed[setting])) - if setting_type == u"": - print u'%s:' % setting - for settin in feed[setting]: - if settin == u'kind': - continue - settin_type = str(type(settin)) - if settin_type == u"": - for setti in settin: - if setti == u'kind': - continue - print convertUTF8(u' %s: %s' % (setti, settin[setti])) - print u'' - elif setting_type == u"": - print u'%s:' % setting - for settin in feed[setting]: - if settin == u'kind': - continue - print convertUTF8(u' %s: %s' % (settin, feed[setting][settin])) - else: - print convertUTF8(u'%s: %s' % (setting, feed[setting])) + printDriveFileData(feed) + +def showDriveFileRevisions(users): + for user in users: + fileId = sys.argv[5] + drive = buildGAPIServiceObject(u'drive', user) + feed = callGAPI(service=drive.revisions(), function=u'list', fileId=fileId) + printDriveFileData(feed) def transferSecCals(users): target_user = sys.argv[5] @@ -9527,6 +9543,8 @@ try: showDriveFileTree(users) elif readWhat == u'fileinfo': showDriveFileInfo(users) + elif readWhat == u'filerevisions': + showDriveFileRevisions(users) elif readWhat == u'sendas': showSendAs(users) elif readWhat == u'gmailprofile': From 0efb736b427fdf0883dce06fdecaa935c5382bb0 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 29 Apr 2016 07:05:40 -0700 Subject: [PATCH 23/80] Have showDriveFileInfo and showDriveFileRevisions use print_json; clean up print_json --- src/gam.py | 53 +++++++++++++++-------------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/src/gam.py b/src/gam.py index a55a20f9..ceaabea3 100755 --- a/src/gam.py +++ b/src/gam.py @@ -3995,45 +3995,19 @@ def downloadDriveFile(users): _, content = drive._http.request(download_url) writeFile(filename, content, continueOnError=True) -def printDriveFileData(feed): - for setting in feed: - if setting in [u'kind', u'etag']: - continue - setting_type = str(type(feed[setting])) - if setting_type == u"": - print u'%s:' % setting - for settin in feed[setting]: - if settin in [u'kind', u'etag']: - continue - settin_type = str(type(settin)) - if settin_type == u"": - for setti in settin: - if setti in [u'kind', u'etag']: - continue - print convertUTF8(u' %s: %s' % (setti, settin[setti])) - print u'' - elif setting_type == u"": - print u'%s:' % setting - for settin in feed[setting]: - if settin in [u'kind', u'etag']: - continue - print convertUTF8(u' %s: %s' % (settin, feed[setting][settin])) - else: - print convertUTF8(u'%s: %s' % (setting, feed[setting])) - def showDriveFileInfo(users): for user in users: fileId = sys.argv[5] drive = buildGAPIServiceObject(u'drive', user) feed = callGAPI(service=drive.files(), function=u'get', fileId=fileId) - printDriveFileData(feed) + print_json(None, feed) def showDriveFileRevisions(users): for user in users: fileId = sys.argv[5] drive = buildGAPIServiceObject(u'drive', user) feed = callGAPI(service=drive.revisions(), function=u'list', fileId=fileId) - printDriveFileData(feed) + print_json(None, feed) def transferSecCals(users): target_user = sys.argv[5] @@ -6782,21 +6756,24 @@ def print_json(object_name, object_value, spacing=u''): return if object_name != None: sys.stdout.write(u'%s%s: ' % (spacing, object_name)) - if type(object_value) is list: - if len(object_value) == 1 and type(object_value[0]) in (str, unicode, int): - sys.stdout.write(u'%s\n' % object_value[0]) + if isinstance(object_value, list): + if len(object_value) == 1 and isinstance(object_value[0], (str, unicode, int, bool)): + sys.stdout.write(convertUTF8(u'%s\n' % object_value[0])) return - sys.stdout.write(u'\n') + if object_name != None: + sys.stdout.write(u'\n') for a_value in object_value: - if type(a_value) in (str, unicode): - print u' %s%s' % (spacing, a_value) + if isinstance(a_value, (str, unicode, int, bool)): + sys.stdout.write(convertUTF8(u' %s%s\n' % (spacing, a_value))) else: - print_json(object_name=None, object_value=a_value, spacing=u' %s' % spacing) - elif type(object_value) is dict: + print_json(None, a_value, u' %s' % spacing) + elif isinstance(object_value, dict): + if object_name != None: + sys.stdout.write(u'\n') for another_object in object_value: - print_json(object_name=another_object, object_value=object_value[another_object], spacing=spacing) + print_json(another_object, object_value[another_object], u' %s' % spacing) else: - sys.stdout.write(u'%s\n' % (object_value)) + sys.stdout.write(convertUTF8(u'%s\n' % (object_value))) def doUpdateNotification(): cd = buildGAPIObject(u'directory') From 9246aed660e5f19dce7448a3e1884c798497b1bf Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 29 Apr 2016 13:01:36 -0700 Subject: [PATCH 24/80] Print formatted permission on add/update drivefileacl instead of JSON data --- src/gam.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/gam.py b/src/gam.py index ceaabea3..b491e39c 100755 --- a/src/gam.py +++ b/src/gam.py @@ -3277,20 +3277,23 @@ def doDriveActivity(users): activity_attributes[0][an_item] = an_item output_csv(activity_attributes, activity_attributes[0], u'Drive Activity', todrive) +def printPermission(permission): + if u'name' in permission: + print convertUTF8(permission[u'name']) + elif (u'id' in permission) and (permission[u'id'] == u'anyone'): + print u'Anyone' + for key in permission: + if key in [u'name', u'kind', u'etag', u'selfLink',]: + continue + print u' %s: %s' % (key, permission[key]) + def showDriveFileACL(users): fileId = sys.argv[5] for user in users: drive = buildGAPIServiceObject(u'drive', user) feed = callGAPI(service=drive.permissions(), function=u'list', fileId=fileId) for permission in feed[u'items']: - try: - print convertUTF8(permission[u'name']) - except KeyError: - pass - for key in permission: - if key in [u'name', u'kind', u'etag', u'selfLink',]: - continue - print u' %s: %s' % (key, permission[key]) + printPermission(permission) print u'' def delDriveFileACL(users): @@ -3347,7 +3350,7 @@ def addDriveFileACL(users): for user in users: drive = buildGAPIServiceObject(u'drive', user) result = callGAPI(service=drive.permissions(), function=u'insert', fileId=fileId, sendNotificationEmails=sendNotificationEmails, emailMessage=emailMessage, body=body) - print result + printPermission(result) def updateDriveFileACL(users): fileId = sys.argv[5] @@ -3387,7 +3390,7 @@ def updateDriveFileACL(users): permissionId = callGAPI(service=drive.permissions(), function=u'getIdForEmail', email=permissionId, fields=u'id')[u'id'] print u'updating permissions for %s to file %s' % (permissionId, fileId) result = callGAPI(service=drive.permissions(), function=u'patch', fileId=fileId, permissionId=permissionId, transferOwnership=transferOwnership, body=body) - print result + printPermission(result) def showDriveFiles(users): files_attr = [{u'Owner': u'Owner',}] From 2fdfc3750ddd3c19bb6534f9697a1f25dd1a5412 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Sat, 30 Apr 2016 07:41:12 -0700 Subject: [PATCH 25/80] Include commands documentation --- src/GamCommands.txt | 675 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 src/GamCommands.txt diff --git a/src/GamCommands.txt b/src/GamCommands.txt new file mode 100644 index 00000000..fbb78395 --- /dev/null +++ b/src/GamCommands.txt @@ -0,0 +1,675 @@ +This document describes the GAM command line syntax in modified BNF, see https://en.wikipedia.org/wiki/Backus-Naur_Form +Items on the command line are space separated, when an actual space character is required, it will be indicated by . +If an item contains spaces, it should be surrounded by " or '. + +[] optional item +() group items +* item may appear zero or more times ++ item may appear one or more times +| separates alternative items + +# Primatives + ::= 0|1|2|3|4|5|6|7|8|9 + ::= + + ::= |a|b|c|d|e|f|A|B|C|D|E|F + ::= an actual space character + ::= a string of characters, surrounded by " or ' if it contains spaces + ::= true|on|yes|enabled|1 += false|off|no|disabled|0 + ::= googleplus|google+|gplus|g+|googledrive|gdrive + ::= Google-Apps|Google-Coordinate|Google-Drive-storage|Google-Vault + ::= apps|gafb|gafw|gams|gau|unlimited|d4w|dfw|coordinate|vault|vfe| + drive-20gb|drive20gb|20gb|drive-50gb|drive50gb|50gb|drive-200gb|drive200gb|200gb|drive-400gb|drive400gb|400gb| + drive-1tb|drive1tb|1tb|drive-2tb|drive2tb|2tb|drive-4tb|drive4tb|4tb|drive-8tb|drive8tb|8tb|drive-16tb|drive16tb|16tb + ::= ascii|mbcs|utf-8|utf-8-sig|utf-16| + ::= pdf|openoffice|ms|microsoft|micro$oft + ::= ar|bn|bg|ca|zh-CN|zh-TW|hr|cs|da|nl|en|en-GB|et|fi|fr|de|el|gu|iw|is|in|it|ja|kn|ko|lv|lt|ms|ml|mr|no|or|fa|pl|pt-BR|pt-PT|ro|ru|sr|sk|sl|es|sv|tl|ta|te|th|tr|uk|ur|vi + +# Basic items built from primatives + ::= | + ::= [M|K|B] + ::= .../ + ::= # + ::= (.)+ + ::= @ + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= -- + ::= --: +