#!/opt/osstech/bin/python2.7
# -*- coding: utf-8 -*-

# googleapps-account-csvusermigrate.py
# - Account migrate on Google Apps by reading CSV file
#
# Copyright (C) TAKEDA Yasuma @ Open Source Solution Technology Co. Ltd.
# License: GNU General Public License version 2 or later
# Date: 2009-09-24, since 2009-03-23

import os, sys, csv, datetime, getopt, string, binascii, sha

# from Google
import gdata.apps.service
import gdata.apps.emailsettings.service

# from OSSTech
import googleappsutils.domain


# Command Option
# -d copy from domain name of Google Apps(Must)
# -t copy to domain name of Google Apps(Must)
# -f csv file(Must)
# -l directory of result file
# -s directory of summary file
# -m directory of migrate file
# -h hashed password

# CSV Format
# This file must be UTF-8 encoding.
# |username|password|
# |user1   |secret  |
# |user2   |secret2 |
#
# If password is {SHA}xxxx or {MD5}xxxx, this field is handled as hashed
# password.


# Migrate file is creating by this command.
# This file is used to migrate user's mail archive from Google to Google
# in next step.
# This file format is here.
# |username|source username  |source password|
# |user1   |user1@example.com|secret         |
# |user2   |user2@example.com|secret2        |
#


def usage():
    sys.stderr.write("Usage:" + sys.argv[0] + " -d <from domain> -t <to domain> -f <csvfile> [-m migratefile] [-l resultfile] [-s summaryfile] [-h]\n")
    sys.exit(1)

def isYes(str):
    if string.upper(str) == "YES":
        return True
    return False

def main():
    fromDomain = None
    toDomain = None
    csvfile = None
    resultfile = None
    summaryfile = None
    migratefile = None
    ishashed = False
    password_hash = None

    # Parse option
    try:
        (opts, args) = getopt.getopt(sys.argv[1:], "d:t:f:l:s:m:h")
    except getopt.GetoptError:
        sys.stderr.write("Error : Invalid Option\n")
        usage()

    for (opt, value) in opts:
        if opt == "-d":
            fromDomain = value
        if opt == "-t":
            toDomain = value
        if opt == "-f":
            csvfile = value
        if opt == "-l":
            resultfile = value
        if opt == "-s":
            summaryfile = value
        if opt == "-m":
            migratefile = value
        if opt == "-h":
            ishashed = True

    if fromDomain == None or toDomain == None or csvfile == None:
        usage()

    # try to open csv file.
    try:
        csvFd = open(csvfile, 'r')
    except Exception, e:
        sys.stderr.write("Error : Failed to open csvfile.\n")
        sys.stderr.write(str(e) + "\n")
        sys.exit(1)

    # Read Configuration
    fromGDomain = googleappsutils.domain.Domain()
    try:
        fromGDomain.readConfig(fromDomain)
    except Exception, e:
        sys.stderr.write("Error : Invalid configuration [" + fromDomain + "]\n")
        sys.stderr.write(str(e))
        sys.exit(1)

    toGDomain = googleappsutils.domain.Domain()
    try:
        toGDomain.readConfig(toDomain)
    except Exception, e:
        sys.stderr.write("Error : Invalid configuration [" + toDomain + "]\n")
        sys.stderr.write(str(e))
        sys.exit(1)

    # Setup result files
    type = "usermigrate"
    now = datetime.datetime.now()
    baseFileName = fromGDomain.getBaseFilename(now, type, fromDomain)
    if resultfile == None:
        resultfile = fromGDomain.resultDir + baseFileName + "-result.csv"
    if summaryfile == None:
        summaryfile = fromGDomain.summaryDir + baseFileName + ".summary"
    if migratefile == None:
        migratefile = fromGDomain.migrateDir + baseFileName + "-migrate.csv"

    try:
        resultFd = open(resultfile, "w")
        summaryFd = open(summaryfile, "w")
        migrateFd = open(migratefile, "w")
    except Exception, e:
        sys.stderr.write("Error : Failed to open file.\n")
        sys.stderr.write(str(e))
        sys.exit(1)

    # Password Hash Type
    password_hash = toGDomain.getPasswordHash()

    # Read entries from csv file
    try:
        reader = csv.reader(csvFd)
    except Exception , e:
        sys.stderr.write("Error : Failed to read entry from csv file.\n")
        sys.stderr.write(str(e)+"\n")
        sys.exit(1)

    # Connect to Google Apps
    try:
        service = gdata.apps.service.AppsService(fromGDomain.admin,fromGDomain.adminPass, fromGDomain.domain)
        service.ProgrammaticLogin()
        emailsettings = gdata.apps.emailsettings.service.EmailSettingsService(fromGDomain.admin, fromGDomain.adminPass,fromGDomain.domain)
        emailsettings.ProgrammaticLogin()
    except Exception, e:
        sys.stderr.write("Error : Failed to connect to Google Apps domain [" + fromDomain + "].\n" + str(e) + "\n")
        sys.exit(1)

    try:
        service2 = gdata.apps.service.AppsService(toGDomain.admin,toGDomain.adminPass, toGDomain.domain)
        service2.ProgrammaticLogin()
        emailsettings2 = gdata.apps.emailsettings.service.EmailSettingsService(toGDomain.admin, toGDomain.adminPass,toGDomain.domain)
        emailsettings2.ProgrammaticLogin()
    except Exception, e:
        sys.stderr.write("Error : Failed to connect to Google Apps domain [" + toDomain + "].\n" + str(e) + "\n")
        sys.exit(1)

    # Handle each entries
    r = 0 # counter of row. 0 is header
    header = []
    error = 0
    success = 0
    for row in reader:
        username = None
        pop = None
        imap = None
        sn = ""
        givenName = ""
        password = ""
        if len(row) == 0:
            continue

        if r == 0:
            # Create header list
            for key in row:
                header.append(key)

            # write header of migrate file
            #migrateFd.write("username,source username,source password\n")
        else:
            # Create user entry
            attrs = {}
            n = 0
            for value in row:
                if n == 0:
                    username = value
                else:
                    attrs[header[n]] = value
                n = n + 1

            # Check ignore user or not
            if fromGDomain.isIgnoreUsers(username):
                continue

            # Check username and password is included in attrs.
            if attrs.has_key("sn"):
                sn = attrs["sn"]
            if attrs.has_key("givenName"):
                givenName = attrs["givenName"]
            if attrs.has_key("password"):
                password = attrs["password"]
            if attrs.has_key("GooglePop"):
                pop = isYes(attrs["GooglePop"])
            if attrs.has_key("GoogleImap"):
                imap = isYes(attrs["GoogleImap"])

            # if password is hashed(OpenLDAP format), hash_function is needed.
            hashMethod = None
            if ishashed:
                try:
                    if password.startswith("{SHA}"):
                        hashMethod = "SHA-1"
                        binData = binascii.a2b_base64(password[5:])
                        hashPassword = binascii.b2a_hex(binData)
                    elif password.startswith("{MD5}"):
                        hashMethod = "MD5"
                        binData = binascii.a2b_base64(password[5:])
                        hashPassword = binascii.b2a_hex(binData)
                except:
                    pass
            else:
                if password_hash == "SHA-1":
                    hashMethod = "SHA-1"
                    hashPassword = binascii.b2a_hex(sha.new(password).digest())
                elif password_hash is None:
                    hashMethod = None
                    hashPassword = password

            # Get user information from Google Apps
            # and change his password with new one and enable IMAP
            if not(len(username) > 0 and len(password) > 0):
                errormsg = username + ",username or password are missing\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
                r = r + 1  # Increment row counter
                continue

            try:
                userEntry = service.RetrieveUser(username)
                userEntry.login = gdata.apps.Login(user_name=username, password=hashPassword, hash_function_name=hashMethod)
                service.UpdateUser(username, userEntry)
                emailsettings.UpdateImap(username,True)
            except gdata.apps.service.AppsForYourDomainException, e:
                errormsg = username + "," + fromGDomain.errorstr(e) + "\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
                r = r + 1  # Increment row counter
                continue
            except Exception , e:
                errormsg = username + "," + '"' + str(e) + '"' + "\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
                r = r + 1  # Increment row counter
                continue

            if len(sn) == 0:
                sn = userEntry.name.family_name
            if len(givenName) == 0:
                givenName = userEntry.name.given_name

            # Check sn,givenName are exists.
            if not(len(sn) > 0 and len(givenName) > 0):
                errormsg = username + ",sn or givenName attributes are missing\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
                r = r + 1  # Increment row counter
                continue

            try:
                # create account to toDomain
                service2.CreateUser(username, sn, givenName, hashPassword, password_hash_function=hashMethod)

                if pop != None:
                    emailsettings2.UpdatePop(username,pop,gdata.apps.emailsettings.service.ALL_MAIL,gdata.apps.emailsettings.service.KEEP)
                if imap != None:
                    emailsettings2.UpdateImap(username,imap)
            except gdata.apps.service.AppsForYourDomainException, e:
                errormsg = username + "," + toGDomain.errorstr(e) + "\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
            except Exception , e:
                errormsg = username + "," + '"' + str(e) + '"' + "\n"
                sys.stderr.write(errormsg)
                resultFd.write(errormsg)
                error = error + 1
            else:
                msg = username + ",OK\n"
                sys.stdout.write(msg)
                resultFd.write(msg)

                # write migrate file
                #line = username + "," + username + "@" + fromDomain + "," + password + "\n"
                line = username + "@" + fromDomain + "#" + password + "," + username + "@" + toDomain
                migrateFd.write(line)
                success = success + 1

        resultFd.flush()
        migrateFd.flush()
        r = r + 1  # Increment row counter

    # Create summary
    msg = str((r-1)) + " user entries are handled.\n"
    summaryFd.write(msg)
    msg = "Success to migrate:" + str(success) + " user entries\n"
    summaryFd.write(msg)
    msg = "Fail to migrate:" + str(error) + " user entries\n"
    summaryFd.write(msg)

    resultFd.close()
    summaryFd.close()
    sys.exit(0)

main()
