mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-03 12:21:35 +00:00
175
src/gam.py
175
src/gam.py
@@ -25,10 +25,10 @@ For more information, see http://git.io/gam
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = u'Jay Lee <jay0lee@gmail.com>'
|
__author__ = u'Jay Lee <jay0lee@gmail.com>'
|
||||||
__version__ = u'3.65'
|
__version__ = u'3.66'
|
||||||
__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, string, StringIO, subprocess
|
import sys, os, time, datetime, random, socket, csv, platform, re, calendar, base64, string, codecs, StringIO, subprocess
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import httplib2
|
import httplib2
|
||||||
@@ -93,8 +93,7 @@ GM_BATCH_QUEUE = u'batq'
|
|||||||
# Extra arguments to pass to GAPI functions
|
# Extra arguments to pass to GAPI functions
|
||||||
GM_EXTRA_ARGS_DICT = u'exad'
|
GM_EXTRA_ARGS_DICT = u'exad'
|
||||||
# Values retrieved from oauth2service.json
|
# Values retrieved from oauth2service.json
|
||||||
GM_OAUTH2SERVICE_KEY = u'oauk'
|
GM_OAUTH2SERVICE_JSON_DATA = u'oajd'
|
||||||
GM_OAUTH2SERVICE_ACCOUNT_EMAIL = u'oaae'
|
|
||||||
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID = u'oaci'
|
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID = u'oaci'
|
||||||
# File containing time of last GAM update check
|
# File containing time of last GAM update check
|
||||||
GM_LAST_UPDATE_CHECK_TXT = u'lupc'
|
GM_LAST_UPDATE_CHECK_TXT = u'lupc'
|
||||||
@@ -114,8 +113,7 @@ GM_Globals = {
|
|||||||
GM_SYS_ENCODING: sys.getfilesystemencoding() if os.name == u'nt' else u'utf-8',
|
GM_SYS_ENCODING: sys.getfilesystemencoding() if os.name == u'nt' else u'utf-8',
|
||||||
GM_BATCH_QUEUE: None,
|
GM_BATCH_QUEUE: None,
|
||||||
GM_EXTRA_ARGS_DICT: {u'prettyPrint': False},
|
GM_EXTRA_ARGS_DICT: {u'prettyPrint': False},
|
||||||
GM_OAUTH2SERVICE_KEY: None,
|
GM_OAUTH2SERVICE_JSON_DATA: None,
|
||||||
GM_OAUTH2SERVICE_ACCOUNT_EMAIL: None,
|
|
||||||
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID: None,
|
GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID: None,
|
||||||
GM_LAST_UPDATE_CHECK_TXT: u'',
|
GM_LAST_UPDATE_CHECK_TXT: u'',
|
||||||
GM_MAP_ORGUNIT_ID_TO_NAME: None,
|
GM_MAP_ORGUNIT_ID_TO_NAME: None,
|
||||||
@@ -254,6 +252,7 @@ MESSAGE_GAM_EXITING_FOR_UPDATE = u'GAM is now exiting so that you can overwrite
|
|||||||
MESSAGE_GAM_OUT_OF_MEMORY = u'GAM has run out of memory. If this is a large Google Apps instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
|
MESSAGE_GAM_OUT_OF_MEMORY = u'GAM has run out of memory. If this is a large Google Apps instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
|
||||||
MESSAGE_HEADER_NOT_FOUND_IN_CSV_HEADERS = u'Header "{0}" not found in CSV headers of "{1}".'
|
MESSAGE_HEADER_NOT_FOUND_IN_CSV_HEADERS = u'Header "{0}" not found in CSV headers of "{1}".'
|
||||||
MESSAGE_HIT_CONTROL_C_TO_UPDATE = u'\n\nHit CTRL+C to visit the GAM website and download the latest release or wait 15 seconds continue with this boring old version. GAM won\'t bother you with this announcement for 1 week or you can create a file named noupdatecheck.txt in the same location as gam.py or gam.exe and GAM won\'t ever check for updates.'
|
MESSAGE_HIT_CONTROL_C_TO_UPDATE = u'\n\nHit CTRL+C to visit the GAM website and download the latest release or wait 15 seconds continue with this boring old version. GAM won\'t bother you with this announcement for 1 week or you can create a file named noupdatecheck.txt in the same location as gam.py or gam.exe and GAM won\'t ever check for updates.'
|
||||||
|
MESSAGE_INVALID_JSON = u'The file {0} has an invalid format.'
|
||||||
MESSAGE_NO_DISCOVERY_INFORMATION = u'No online discovery doc and {0} does not exist locally'
|
MESSAGE_NO_DISCOVERY_INFORMATION = u'No online discovery doc and {0} does not exist locally'
|
||||||
MESSAGE_NO_PYTHON_SSL = u'You don\'t have the Python SSL module installed so we can\'t verify SSL Certificates. You can fix this by installing the Python SSL module or you can live on the edge and turn SSL validation off by creating a file named noverifyssl.txt in the same location as gam.exe / gam.py'
|
MESSAGE_NO_PYTHON_SSL = u'You don\'t have the Python SSL module installed so we can\'t verify SSL Certificates. You can fix this by installing the Python SSL module or you can live on the edge and turn SSL validation off by creating a file named noverifyssl.txt in the same location as gam.exe / gam.py'
|
||||||
MESSAGE_NO_TRANSFER_LACK_OF_DISK_SPACE = u'Cowardly refusing to perform migration due to lack of target drive space. Source size: {0}mb Target Free: {1}mb'
|
MESSAGE_NO_TRANSFER_LACK_OF_DISK_SPACE = u'Cowardly refusing to perform migration due to lack of target drive space. Source size: {0}mb Target Free: {1}mb'
|
||||||
@@ -357,17 +356,32 @@ gam.exe update group announcements add member jsmith
|
|||||||
#
|
#
|
||||||
# Error handling
|
# Error handling
|
||||||
#
|
#
|
||||||
|
def stderrErrorMsg(message):
|
||||||
|
sys.stderr.write(convertUTF8(u'\n{0}{1}\n'.format(ERROR_PREFIX, message)))
|
||||||
|
|
||||||
|
def stderrWarningMsg(message):
|
||||||
|
sys.stderr.write(convertUTF8(u'\n{0}{1}\n'.format(WARNING_PREFIX, message)))
|
||||||
|
|
||||||
def systemErrorExit(sysRC, message):
|
def systemErrorExit(sysRC, message):
|
||||||
if message:
|
if message:
|
||||||
sys.stderr.write(u'\n{0}{1}\n'.format(ERROR_PREFIX, message))
|
stderrErrorMsg(message)
|
||||||
sys.exit(sysRC)
|
sys.exit(sysRC)
|
||||||
|
|
||||||
|
def invalidJSONExit(fileName):
|
||||||
|
systemErrorExit(17, MESSAGE_INVALID_JSON.format(fileName))
|
||||||
|
|
||||||
def noPythonSSLExit():
|
def noPythonSSLExit():
|
||||||
systemErrorExit(8, MESSAGE_NO_PYTHON_SSL)
|
systemErrorExit(8, MESSAGE_NO_PYTHON_SSL)
|
||||||
|
|
||||||
def printLine(message):
|
def printLine(message):
|
||||||
sys.stdout.write(message+u'\n')
|
sys.stdout.write(message+u'\n')
|
||||||
#
|
#
|
||||||
|
def getCharSet(i):
|
||||||
|
if (i == len(sys.argv)) or (sys.argv[i].lower() != u'charset'):
|
||||||
|
return (i, GC_Values.get(GC_CHARSET, GM_Globals[GM_SYS_ENCODING]))
|
||||||
|
return (i+2, sys.argv[i+1])
|
||||||
|
|
||||||
|
#
|
||||||
# Open a file
|
# Open a file
|
||||||
#
|
#
|
||||||
def openFile(filename, mode=u'rb'):
|
def openFile(filename, mode=u'rb'):
|
||||||
@@ -387,22 +401,29 @@ def closeFile(f):
|
|||||||
f.close()
|
f.close()
|
||||||
return True
|
return True
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
stderrErrorMsg(e)
|
||||||
return False
|
return False
|
||||||
#
|
#
|
||||||
# Read a file
|
# Read a file
|
||||||
#
|
#
|
||||||
def readFile(filename, mode=u'rb', continueOnError=False, displayError=True):
|
def readFile(filename, mode=u'rb', continueOnError=False, displayError=True, encoding=None):
|
||||||
try:
|
try:
|
||||||
if filename != u'-':
|
if filename != u'-':
|
||||||
|
if not encoding:
|
||||||
with open(filename, mode) as f:
|
with open(filename, mode) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
else:
|
||||||
|
with codecs.open(filename, mode, encoding) as f:
|
||||||
|
content = f.read()
|
||||||
|
if not content.startswith(codecs.BOM_UTF8):
|
||||||
|
return content
|
||||||
|
return content.replace(codecs.BOM_UTF8, u'', 1)
|
||||||
else:
|
else:
|
||||||
return unicode(sys.stdin.read())
|
return unicode(sys.stdin.read())
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
if continueOnError:
|
if continueOnError:
|
||||||
if displayError:
|
if displayError:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(WARNING_PREFIX, e))
|
stderrWarningMsg(e)
|
||||||
return None
|
return None
|
||||||
systemErrorExit(6, e)
|
systemErrorExit(6, e)
|
||||||
#
|
#
|
||||||
@@ -416,7 +437,7 @@ def writeFile(filename, data, mode=u'wb', continueOnError=False, displayError=Tr
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
if continueOnError:
|
if continueOnError:
|
||||||
if displayError:
|
if displayError:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
stderrErrorMsg(e)
|
||||||
return False
|
return False
|
||||||
systemErrorExit(6, e)
|
systemErrorExit(6, e)
|
||||||
#
|
#
|
||||||
@@ -494,8 +515,7 @@ def SetGlobalVariables():
|
|||||||
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
||||||
doGAMCheckForUpdates()
|
doGAMCheckForUpdates()
|
||||||
# Globals derived from config file values
|
# Globals derived from config file values
|
||||||
GM_Globals[GM_OAUTH2SERVICE_KEY] = None
|
GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = None
|
||||||
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL] = None
|
|
||||||
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = None
|
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = None
|
||||||
GM_Globals[GM_EXTRA_ARGS_DICT] = {u'prettyPrint': GC_Values[GC_DEBUG_LEVEL] > 0}
|
GM_Globals[GM_EXTRA_ARGS_DICT] = {u'prettyPrint': GC_Values[GC_DEBUG_LEVEL] > 0}
|
||||||
httplib2.debuglevel = GC_Values[GC_DEBUG_LEVEL]
|
httplib2.debuglevel = GC_Values[GC_DEBUG_LEVEL]
|
||||||
@@ -647,7 +667,7 @@ def callGData(service, function, soft_errors=False, throw_errors=[], **kwargs):
|
|||||||
if n > 3:
|
if n > 3:
|
||||||
sys.stderr.write(u'attempt %s/%s\n' % (n+1, retries))
|
sys.stderr.write(u'attempt %s/%s\n' % (n+1, retries))
|
||||||
continue
|
continue
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, terminating_error))
|
stderrErrorMsg(terminating_error)
|
||||||
if soft_errors:
|
if soft_errors:
|
||||||
if n != 1:
|
if n != 1:
|
||||||
sys.stderr.write(u' - Giving up.\n')
|
sys.stderr.write(u' - Giving up.\n')
|
||||||
@@ -672,7 +692,7 @@ def callGAPI(service, function, silent_errors=False, soft_errors=False, throw_re
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
if not silent_errors:
|
if not silent_errors:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e.content))
|
stderrErrorMsg(e.content)
|
||||||
if soft_errors:
|
if soft_errors:
|
||||||
return None
|
return None
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
@@ -694,14 +714,14 @@ def callGAPI(service, function, silent_errors=False, soft_errors=False, throw_re
|
|||||||
if n > 3:
|
if n > 3:
|
||||||
sys.stderr.write(u'attempt %s/%s\n' % (n+1, retries))
|
sys.stderr.write(u'attempt %s/%s\n' % (n+1, retries))
|
||||||
continue
|
continue
|
||||||
sys.stderr.write(u'{0}{1}: {2} - {3}\n'.format(ERROR_PREFIX, http_status, message, reason))
|
stderrErrorMsg(u'{0}: {1} - {2}'.format(http_status, message, reason))
|
||||||
if soft_errors:
|
if soft_errors:
|
||||||
if n != 1:
|
if n != 1:
|
||||||
sys.stderr.write(u' - Giving up.\n')
|
sys.stderr.write(u' - Giving up.\n')
|
||||||
return None
|
return None
|
||||||
sys.exit(int(http_status))
|
sys.exit(int(http_status))
|
||||||
except oauth2client.client.AccessTokenRefreshError, e:
|
except oauth2client.client.AccessTokenRefreshError, e:
|
||||||
sys.stderr.write(u'{0}Authentication Token Error: {1}\n'.format(ERROR_PREFIX, e))
|
stderrErrorMsg(u'Authentication Token Error: {0}'.format(e))
|
||||||
sys.exit(403)
|
sys.exit(403)
|
||||||
except httplib2.CertificateValidationUnsupported:
|
except httplib2.CertificateValidationUnsupported:
|
||||||
noPythonSSLExit()
|
noPythonSSLExit()
|
||||||
@@ -841,18 +861,24 @@ def buildGAPIObject(api):
|
|||||||
return service
|
return service
|
||||||
|
|
||||||
def buildGAPIServiceObject(api, act_as, soft_errors=False):
|
def buildGAPIServiceObject(api, act_as, soft_errors=False):
|
||||||
if not GM_Globals[GM_OAUTH2SERVICE_KEY]:
|
try:
|
||||||
|
if not GM_Globals[GM_OAUTH2SERVICE_JSON_DATA]:
|
||||||
json_string = readFile(GC_Values[GC_OAUTH2SERVICE_JSON], continueOnError=True, displayError=True)
|
json_string = readFile(GC_Values[GC_OAUTH2SERVICE_JSON], continueOnError=True, displayError=True)
|
||||||
if not json_string:
|
if not json_string:
|
||||||
printLine(MESSAGE_WIKI_INSTRUCTIONS_OAUTH2SERVICE_JSON)
|
printLine(MESSAGE_WIKI_INSTRUCTIONS_OAUTH2SERVICE_JSON)
|
||||||
printLine(GAM_WIKI_CREATE_CLIENT_SECRETS)
|
printLine(GAM_WIKI_CREATE_CLIENT_SECRETS)
|
||||||
systemErrorExit(6, None)
|
systemErrorExit(6, None)
|
||||||
json_data = json.loads(json_string)
|
GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = json.loads(json_string)
|
||||||
scopes = getAPIScope(api)
|
scopes = getAPIScope(api)
|
||||||
credentials = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_dict(
|
credentials = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_dict(GM_Globals[GM_OAUTH2SERVICE_JSON_DATA], scopes)
|
||||||
json_data, scopes)
|
|
||||||
credentials = credentials.create_delegated(act_as)
|
credentials = credentials.create_delegated(act_as)
|
||||||
credentials.user_agent = GAM_INFO
|
credentials.user_agent = GAM_INFO
|
||||||
|
serialization_data = credentials.serialization_data
|
||||||
|
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = serialization_data[u'client_id']
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
printLine(MESSAGE_WIKI_INSTRUCTIONS_OAUTH2SERVICE_JSON)
|
||||||
|
printLine(GAM_WIKI_CREATE_CLIENT_SECRETS)
|
||||||
|
invalidJSONExit(GC_Values[GC_OAUTH2SERVICE_JSON])
|
||||||
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
||||||
cache=GC_Values[GC_CACHE_DIR]))
|
cache=GC_Values[GC_CACHE_DIR]))
|
||||||
version = getAPIVer(api)
|
version = getAPIVer(api)
|
||||||
@@ -866,8 +892,8 @@ def buildGAPIServiceObject(api, act_as, soft_errors=False):
|
|||||||
if e.message in [u'access_denied',
|
if e.message in [u'access_denied',
|
||||||
u'unauthorized_client: Unauthorized client or scope in request.',
|
u'unauthorized_client: Unauthorized client or scope in request.',
|
||||||
u'access_denied: Requested client not authorized.']:
|
u'access_denied: Requested client not authorized.']:
|
||||||
systemErrorExit(5, MESSAGE_CLIENT_API_ACCESS_DENIED.format(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID], u','.join(scope)))
|
systemErrorExit(5, MESSAGE_CLIENT_API_ACCESS_DENIED.format(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID], u','.join(scopes)))
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
stderrErrorMsg(e)
|
||||||
if soft_errors:
|
if soft_errors:
|
||||||
return False
|
return False
|
||||||
sys.exit(4)
|
sys.exit(4)
|
||||||
@@ -888,7 +914,7 @@ def buildDiscoveryObject(api):
|
|||||||
try:
|
try:
|
||||||
return json.loads(content)
|
return json.loads(content)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
sys.stderr.write(u'{0}Failed to parse as JSON: {1}\n'.format(ERROR_PREFIX, content))
|
stderrErrorMsg(u'Failed to parse as JSON: {0}'.format(content))
|
||||||
raise googleapiclient.errors.InvalidJsonError()
|
raise googleapiclient.errors.InvalidJsonError()
|
||||||
|
|
||||||
def commonAppsObjInit(appsObj):
|
def commonAppsObjInit(appsObj):
|
||||||
@@ -1172,22 +1198,22 @@ def doDelegates(users):
|
|||||||
delegate_user_details = callGAPI(service=cd.users(), function=u'get', userKey=delegate_email)
|
delegate_user_details = callGAPI(service=cd.users(), function=u'get', userKey=delegate_email)
|
||||||
delegator_user_details = callGAPI(service=cd.users(), function=u'get', userKey=delegator_email)
|
delegator_user_details = callGAPI(service=cd.users(), function=u'get', userKey=delegator_email)
|
||||||
if delegate_user_details[u'suspended'] == True:
|
if delegate_user_details[u'suspended'] == True:
|
||||||
sys.stderr.write(u'ERROR: User %s is suspended. You must unsuspend for delegation.\n' % delegate_email)
|
stderrErrorMsg(u'User {0} is suspended. You must unsuspend for delegation.'.format(delegate_email))
|
||||||
if delete_alias:
|
if delete_alias:
|
||||||
doDeleteAlias(alias_email=use_delegate_address)
|
doDeleteAlias(alias_email=use_delegate_address)
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
if delegator_user_details[u'suspended'] == True:
|
if delegator_user_details[u'suspended'] == True:
|
||||||
sys.stderr.write(u'ERROR: User %s is suspended. You must unsuspend for delegation.\n' % delegator_email)
|
stderrErrorMsg(u'User {0} is suspended. You must unsuspend for delegation.'.format(delegator_email))
|
||||||
if delete_alias:
|
if delete_alias:
|
||||||
doDeleteAlias(alias_email=use_delegate_address)
|
doDeleteAlias(alias_email=use_delegate_address)
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
if delegate_user_details[u'changePasswordAtNextLogin'] == True:
|
if delegate_user_details[u'changePasswordAtNextLogin'] == True:
|
||||||
sys.stderr.write(u'ERROR: User %s is required to change password at next login. You must change password or clear changepassword flag for delegation.\n' % delegate_email)
|
stderrErrorMsg(u'User {0} is required to change password at next login. You must change password or clear changepassword flag for delegation.'.format(delegate_email))
|
||||||
if delete_alias:
|
if delete_alias:
|
||||||
doDeleteAlias(alias_email=use_delegate_address)
|
doDeleteAlias(alias_email=use_delegate_address)
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
if delegator_user_details[u'changePasswordAtNextLogin'] == True:
|
if delegator_user_details[u'changePasswordAtNextLogin'] == True:
|
||||||
sys.stderr.write(u'ERROR: User %s is required to change password at next login. You must change password or clear changepassword flag for delegation.\n' % delegator_email)
|
stderrErrorMsg(u'User {0} is required to change password at next login. You must change password or clear changepassword flag for delegation.'.format(delegator_email))
|
||||||
if delete_alias:
|
if delete_alias:
|
||||||
doDeleteAlias(alias_email=use_delegate_address)
|
doDeleteAlias(alias_email=use_delegate_address)
|
||||||
sys.exit(5)
|
sys.exit(5)
|
||||||
@@ -4441,18 +4467,19 @@ def doLabel(users):
|
|||||||
i += 1
|
i += 1
|
||||||
callGAPI(service=gmail.users().labels(), function=u'create', soft_errors=True, userId=user, body=body)
|
callGAPI(service=gmail.users().labels(), function=u'create', soft_errors=True, userId=user, body=body)
|
||||||
|
|
||||||
PROCESS_MESSAGE_FUNCTION_TO_ACTION_MAP = {u'delete': u'deleted',
|
PROCESS_MESSAGE_FUNCTION_TO_ACTION_MAP = {u'delete': u'deleted', u'trash': u'trashed', u'untrash': u'untrashed', u'modify': u'modified'}
|
||||||
u'trash': u'trashed', u'untrash': u'untrashed', u'modify': u'modified'}
|
|
||||||
|
|
||||||
def labelsToLabelIds(gmail, labels):
|
def labelsToLabelIds(gmail, labels):
|
||||||
allLabels = {u'INBOX': u'INBOX', u'SPAM': u'SPAM', u'TRASH': u'TRASH',
|
allLabels = {
|
||||||
|
u'INBOX': u'INBOX', u'SPAM': u'SPAM', u'TRASH': u'TRASH',
|
||||||
u'UNREAD': u'UNREAD', u'STARRED': u'STARRED', u'IMPORTANT': u'IMPORTANT',
|
u'UNREAD': u'UNREAD', u'STARRED': u'STARRED', u'IMPORTANT': u'IMPORTANT',
|
||||||
u'SENT': u'SENT', u'DRAFT': u'DRAFT',
|
u'SENT': u'SENT', u'DRAFT': u'DRAFT',
|
||||||
u'CATEGORY_PERSONAL': u'CATEGORY_PERSONAL',
|
u'CATEGORY_PERSONAL': u'CATEGORY_PERSONAL',
|
||||||
u'CATEGORY_SOCIAL': u'CATEGORY_SOCIAL',
|
u'CATEGORY_SOCIAL': u'CATEGORY_SOCIAL',
|
||||||
u'CATEGORY_PROMOTIONS': u'CATEGORY_PROMOTIONS',
|
u'CATEGORY_PROMOTIONS': u'CATEGORY_PROMOTIONS',
|
||||||
u'CATEGORY_UPDATES': u'CATEGORY_UPDATES',
|
u'CATEGORY_UPDATES': u'CATEGORY_UPDATES',
|
||||||
u'CATEGORY_FORUMS': u'CATEGORY_FORUMS'}
|
u'CATEGORY_FORUMS': u'CATEGORY_FORUMS',
|
||||||
|
}
|
||||||
labelIds = list()
|
labelIds = list()
|
||||||
for label in labels:
|
for label in labels:
|
||||||
if label not in allLabels:
|
if label not in allLabels:
|
||||||
@@ -4466,7 +4493,7 @@ def labelsToLabelIds(gmail, labels):
|
|||||||
allLabels[a_label['name']] = a_label['id']
|
allLabels[a_label['name']] = a_label['id']
|
||||||
if label not in allLabels:
|
if label not in allLabels:
|
||||||
# if still not there, create it
|
# if still not there, create it
|
||||||
label_results = callGAPI(service=gmail.users().labels(), function='create',
|
label_results = callGAPI(gmail.users().labels(), 'create',
|
||||||
body={'labelListVisibility': 'labelShow',
|
body={'labelListVisibility': 'labelShow',
|
||||||
'messageListVisibility': 'show', 'name': label},
|
'messageListVisibility': 'show', 'name': label},
|
||||||
userId='me', fields='id')
|
userId='me', fields='id')
|
||||||
@@ -4480,8 +4507,8 @@ def labelsToLabelIds(gmail, labels):
|
|||||||
parent_label = label[:label.rfind('/')]
|
parent_label = label[:label.rfind('/')]
|
||||||
while True:
|
while True:
|
||||||
if not parent_label in allLabels:
|
if not parent_label in allLabels:
|
||||||
label_result = callGAPI(service=gmail.users().labels(),
|
label_result = callGAPI(gmail.users().labels(), 'create',
|
||||||
function='create', userId='me', body={'name': parent_label})
|
userId='me', body={'name': parent_label})
|
||||||
allLabels[parent_label] = label_result['id']
|
allLabels[parent_label] = label_result['id']
|
||||||
if parent_label.find('/') == -1:
|
if parent_label.find('/') == -1:
|
||||||
break
|
break
|
||||||
@@ -4492,36 +4519,25 @@ def doProcessMessages(users, function):
|
|||||||
query = None
|
query = None
|
||||||
doIt = False
|
doIt = False
|
||||||
maxToProcess = 1
|
maxToProcess = 1
|
||||||
body = None
|
body = {}
|
||||||
i = 5
|
i = 5
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
if sys.argv[i].lower() == u'query':
|
myarg = sys.argv[i].lower().replace(u'_', u'')
|
||||||
|
if myarg == u'query':
|
||||||
query = sys.argv[i+1]
|
query = sys.argv[i+1]
|
||||||
i += 2
|
i += 2
|
||||||
elif sys.argv[i].lower() == u'doit':
|
elif myarg == u'doit':
|
||||||
doIt = True
|
doIt = True
|
||||||
i += 1
|
i += 1
|
||||||
elif sys.argv[i].lower().replace(u'_', u'') in [u'maxtodelete', u'maxtotrash', u'maxtomodify', u'maxtountrash']:
|
elif myarg in [u'maxtodelete', u'maxtotrash', u'maxtomodify', u'maxtountrash']:
|
||||||
maxToProcess = int(sys.argv[i+1])
|
maxToProcess = int(sys.argv[i+1])
|
||||||
i += 2
|
i += 2
|
||||||
elif sys.argv[i].lower().replace(u'_', u'') in [u'addlabel']:
|
elif (function == u'modify') and (myarg == u'addlabel'):
|
||||||
if function != u'modify':
|
body.setdefault(u'addLabelIds', [])
|
||||||
print u'ERROR: labels can only be added on modify, not %s' % function
|
|
||||||
sys.exit(3)
|
|
||||||
if not body:
|
|
||||||
body = {u'addLabelIds': []}
|
|
||||||
if u'addLabelIds' not in body:
|
|
||||||
body[u'addLabelIds'] = []
|
|
||||||
body[u'addLabelIds'].append(sys.argv[i+1])
|
body[u'addLabelIds'].append(sys.argv[i+1])
|
||||||
i += 2
|
i += 2
|
||||||
elif sys.argv[i].lower().replace(u'_', u'') in [u'removelabel']:
|
elif (function == u'modify') and (myarg == u'removelabel'):
|
||||||
if function != u'modify':
|
body.setdefault(u'removeLabelIds', [])
|
||||||
print u'ERROR: labels can only be removed on modify, not %s' % function
|
|
||||||
sys.exit(3)
|
|
||||||
if not body:
|
|
||||||
body = {u'removeLabelIds': []}
|
|
||||||
if u'removeLabelIds' not in body:
|
|
||||||
body[u'removeLabelIds'] = []
|
|
||||||
body[u'removeLabelIds'].append(sys.argv[i+1])
|
body[u'removeLabelIds'].append(sys.argv[i+1])
|
||||||
i += 2
|
i += 2
|
||||||
else:
|
else:
|
||||||
@@ -4545,12 +4561,6 @@ def doProcessMessages(users, function):
|
|||||||
elif result_count > maxToProcess:
|
elif result_count > maxToProcess:
|
||||||
print u'WARNING: refusing to %s ANY messages for %s since max messages to process is %s and messages to be %s is %s\n' % (function, user, maxToProcess, action, result_count)
|
print u'WARNING: refusing to %s ANY messages for %s since max messages to process is %s and messages to be %s is %s\n' % (function, user, maxToProcess, action, result_count)
|
||||||
continue
|
continue
|
||||||
if not body:
|
|
||||||
kwargs = {}
|
|
||||||
else:
|
|
||||||
kwargs = {u'body': {}}
|
|
||||||
for my_key in body.keys():
|
|
||||||
kwargs[u'body'][my_key] = labelsToLabelIds(gmail, body[my_key])
|
|
||||||
i = 0
|
i = 0
|
||||||
if function == u'delete':
|
if function == u'delete':
|
||||||
id_batches = [[]]
|
id_batches = [[]]
|
||||||
@@ -4567,6 +4577,12 @@ def doProcessMessages(users, function):
|
|||||||
deleted_messages += len(id_batch)
|
deleted_messages += len(id_batch)
|
||||||
print u'deleted %s of %s messages' % (deleted_messages, result_count)
|
print u'deleted %s of %s messages' % (deleted_messages, result_count)
|
||||||
continue
|
continue
|
||||||
|
if not body:
|
||||||
|
kwargs = {}
|
||||||
|
else:
|
||||||
|
kwargs = {u'body': {}}
|
||||||
|
for my_key in body.keys():
|
||||||
|
kwargs[u'body'][my_key] = labelsToLabelIds(gmail, body[my_key])
|
||||||
for a_message in listResult:
|
for a_message in listResult:
|
||||||
i += 1
|
i += 1
|
||||||
print u' %s message %s for user %s (%s/%s)' % (function, a_message[u'id'], user, i, result_count)
|
print u' %s message %s for user %s (%s/%s)' % (function, a_message[u'id'], user, i, result_count)
|
||||||
@@ -4575,6 +4591,7 @@ def doProcessMessages(users, function):
|
|||||||
|
|
||||||
def doDeleteLabel(users):
|
def doDeleteLabel(users):
|
||||||
label = sys.argv[5]
|
label = sys.argv[5]
|
||||||
|
label_name_lower = label.lower()
|
||||||
for user in users:
|
for user in users:
|
||||||
gmail = buildGAPIServiceObject(u'gmail', user)
|
gmail = buildGAPIServiceObject(u'gmail', user)
|
||||||
print u'Getting all labels for %s...' % user
|
print u'Getting all labels for %s...' % user
|
||||||
@@ -4594,13 +4611,11 @@ def doDeleteLabel(users):
|
|||||||
elif p.match(del_label[u'name']):
|
elif p.match(del_label[u'name']):
|
||||||
del_labels.append(del_label)
|
del_labels.append(del_label)
|
||||||
else:
|
else:
|
||||||
got_label = False
|
|
||||||
for del_label in labels[u'labels']:
|
for del_label in labels[u'labels']:
|
||||||
if label.lower() == del_label[u'name'].lower():
|
if label_name_lower == del_label[u'name'].lower():
|
||||||
del_labels.append(del_label)
|
del_labels.append(del_label)
|
||||||
got_label = True
|
|
||||||
break
|
break
|
||||||
if not got_label:
|
else:
|
||||||
print u' Error: no such label for %s' % user
|
print u' Error: no such label for %s' % user
|
||||||
continue
|
continue
|
||||||
del_me_count = len(del_labels)
|
del_me_count = len(del_labels)
|
||||||
@@ -4670,6 +4685,7 @@ def showGmailProfile(users):
|
|||||||
|
|
||||||
def updateLabels(users):
|
def updateLabels(users):
|
||||||
label_name = sys.argv[5]
|
label_name = sys.argv[5]
|
||||||
|
label_name_lower = label_name.lower()
|
||||||
body = {}
|
body = {}
|
||||||
i = 6
|
i = 6
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
@@ -4699,14 +4715,13 @@ def updateLabels(users):
|
|||||||
for user in users:
|
for user in users:
|
||||||
gmail = buildGAPIServiceObject(u'gmail', user)
|
gmail = buildGAPIServiceObject(u'gmail', user)
|
||||||
labels = callGAPI(service=gmail.users().labels(), function=u'list', userId=user, fields=u'labels(id,name)')
|
labels = callGAPI(service=gmail.users().labels(), function=u'list', userId=user, fields=u'labels(id,name)')
|
||||||
label_id = None
|
|
||||||
for label in labels[u'labels']:
|
for label in labels[u'labels']:
|
||||||
if label[u'name'].lower() == label_name.lower():
|
if label[u'name'].lower() == label_name_lower:
|
||||||
label_id = label[u'id']
|
callGAPI(service=gmail.users().labels(), function=u'patch', soft_errors=True,
|
||||||
|
userId=user, id=label[u'id'], body=body)
|
||||||
break
|
break
|
||||||
if not label_id:
|
else:
|
||||||
print 'Error: user does not have a label named %s' % label_name
|
print 'Error: user does not have a label named %s' % label_name
|
||||||
callGAPI(service=gmail.users().labels(), function=u'patch', soft_errors=True, userId=user, id=label_id, body=body)
|
|
||||||
|
|
||||||
def renameLabels(users):
|
def renameLabels(users):
|
||||||
search = u'^Inbox/(.*)$'
|
search = u'^Inbox/(.*)$'
|
||||||
@@ -4903,10 +4918,14 @@ def getForward(users):
|
|||||||
|
|
||||||
def doSignature(users):
|
def doSignature(users):
|
||||||
import cgi
|
import cgi
|
||||||
if sys.argv[4].lower() == u'file':
|
i = 4
|
||||||
signature = cgi.escape(readFile(sys.argv[5]).replace(u'\\n', u'
').replace(u'"', u"'"))
|
if sys.argv[i].lower() == u'file':
|
||||||
|
filename = sys.argv[i+1]
|
||||||
|
i += 2
|
||||||
|
i, encoding = getCharSet(i)
|
||||||
|
signature = readFile(filename, encoding=encoding).replace(u'\\n', u'
').replace(u'\n', u'<br/>')
|
||||||
else:
|
else:
|
||||||
signature = cgi.escape(sys.argv[4]).replace(u'\\n', u'
').replace(u'"', u"'")
|
signature = cgi.escape(sys.argv[i]).replace(u'\\n', u'
').replace(u'"', u"'")
|
||||||
xmlsig = u'''<?xml version="1.0" encoding="utf-8"?>
|
xmlsig = u'''<?xml version="1.0" encoding="utf-8"?>
|
||||||
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">
|
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">
|
||||||
<apps:property name="signature" value="%s" />
|
<apps:property name="signature" value="%s" />
|
||||||
@@ -4993,8 +5012,10 @@ def doVacation(users):
|
|||||||
end_date = sys.argv[i+1]
|
end_date = sys.argv[i+1]
|
||||||
i += 2
|
i += 2
|
||||||
elif sys.argv[i].lower() == u'file':
|
elif sys.argv[i].lower() == u'file':
|
||||||
message = readFile(sys.argv[i+1])
|
filename = sys.argv[i+1]
|
||||||
i += 2
|
i += 2
|
||||||
|
i, encoding = getCharSet(i)
|
||||||
|
message = readFile(filename, encoding=encoding)
|
||||||
else:
|
else:
|
||||||
print u'ERROR: %s is not a valid argument for "gam <users> vacation"' % sys.argv[i]
|
print u'ERROR: %s is not a valid argument for "gam <users> vacation"' % sys.argv[i]
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -8908,7 +8929,7 @@ def doDeleteOAuth():
|
|||||||
try:
|
try:
|
||||||
credentials.revoke(http)
|
credentials.revoke(http)
|
||||||
except oauth2client.client.TokenRevokeError, e:
|
except oauth2client.client.TokenRevokeError, e:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e.message))
|
stderrErrorMsg(e.message)
|
||||||
os.remove(GC_Values[GC_OAUTH2_TXT])
|
os.remove(GC_Values[GC_OAUTH2_TXT])
|
||||||
|
|
||||||
class cmd_flags(object):
|
class cmd_flags(object):
|
||||||
@@ -9690,8 +9711,8 @@ except IndexError:
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
sys.exit(50)
|
sys.exit(50)
|
||||||
except socket.error, e:
|
except socket.error, e:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
stderrErrorMsg(e)
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
except MemoryError:
|
except MemoryError:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, MESSAGE_GAM_OUT_OF_MEMORY))
|
stderrErrorMsg(MESSAGE_GAM_OUT_OF_MEMORY)
|
||||||
sys.exit(99)
|
sys.exit(99)
|
||||||
|
|||||||
@@ -215,11 +215,8 @@ def run_flow(flow, storage, flags=None, http=None):
|
|||||||
print()
|
print()
|
||||||
print(' ' + authorize_url)
|
print(' ' + authorize_url)
|
||||||
print()
|
print()
|
||||||
print('If your browser is on a different machine then '
|
print('If your browser is on a different machine then exit and re-run this')
|
||||||
'exit and re-run this')
|
print('after creating a file called nobrowser.txt in the same path as GAM.')
|
||||||
print('application with the command-line parameter ')
|
|
||||||
print()
|
|
||||||
print(' --noauth_local_webserver')
|
|
||||||
print()
|
print()
|
||||||
else:
|
else:
|
||||||
print('Go to the following link in your browser:')
|
print('Go to the following link in your browser:')
|
||||||
|
|||||||
Reference in New Issue
Block a user