---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by hartnett.
--- DateTime: 2019/11/4 20:44
---

local ngx = ngx
local string = string
local http = require("resty.http")
local redis = require("apisix.core.redis")
local core = require("apisix.core")

local basic_auth = require("apisix.plugins.exchange-mobile.basic_auth")
local util = require("apisix.plugins.exchange-mobile.util")
local device_manager = require("apisix.plugins.exchange-mobile.device_manager")

local fetch_local_conf = require("apisix.core.config_local").local_conf
local config = fetch_local_conf()

local wbxml_api = config.api.wbxml_api

-- 拦截的指令列表
local cmd_list = {
    -- request阶段拦截的指令列表
    req = {
        Sync = true,
        SendMail = true,
        FolderCreate = true,
        FolderDelete = true,
        FolderUpdate = true,
        MeetingResponse = true,
        ItemOperations = true,
        SmartForward = true,
        SmartReply = true,
        MoveItems = true,
    },
    -- response阶段拦截的指令列表
    resp = {
        -- Sync = true,
        Search = true,
        GetAttachment = true,
        GetItemEstimate = true,
        MeetingResponse = true,
        -- FolderSync = true,
    }
}

-- 检测指令是否需要拦截
local function check_cmd(cmd, cmd_types)
    local result = false
    if cmd_types[cmd] then
        result = true
    end
    core.log.warn(string.format("cmd: %s, cmd_types: %s, result: %s",
            cmd, cmd_types, result))

    return result
end



-- 登录频率限制，每分钟只允许5次（每登录一次会有3个请求）
local function login_times_limit(full_username, ad_domain_name)
    if ngx.var.request_method == "OPTIONS" and ngx.var.uri == "/Microsoft-Server-ActiveSync" then
        local user = basic_auth.get_username_from_ad_account(full_username, ad_domain_name)
        local key = string.format("login_%s", user)
        local redis_cli, err = redis.new()
        core.log.warn(string.format("redis client: %s, err: %s", redis_cli, err))
        local res, err = redis_cli:hmget(key, "times")
        local times = 1
        if err == nil and res ~= nil then
            if type(res[1]) == "string" then
                times = tonumber(res[1])
                if times > 15 then
                    -- 超出登录次数限制
                    ngx.exit(ngx.HTTP_OK)
                else
                    times = times + 1
                    redis_cli:hmset(key, "times", times)
                end
            end
        else
            redis_cli:hmset(key, "times", times)
            redis_cli:expire(key, 60)
        end
    end
end

-- get username from headers
local function get_username_from_header(ctx)
    local authorization = core.request.header(ctx, "authorization")
    local username = basic_auth.get_username_from_basic_auth(authorization)
    return username
end

-- 从authorization中获取用户名
local function get_user_from_header(ctx, domain)
    local full_user = get_username_from_header(ctx)
    local user = basic_auth.get_username_from_ad_account(full_user, domain)
    return user
end



-- 获取wbxml信息
local function get_wbxml_data(device_id)
    local device_info = {}

    ngx.req.read_body()

    local data = ngx.req.get_body_data()
    if data and #data < 200 then
        local headers = { ["Content-Type"] = "application/octet-stream" }

        local http_client = http.new()
        local res, err = http_client:request_uri(wbxml_api, {
            method = "POST",
            body = data,
            headers = headers,
        })

        if res then
            core.log.info(string.format("res: %s, err:%s, status_code: %s, result: %s",
                    res, err, res.status_code, res.text))
        end

        if res and res.status == 200 then
            local result = res.body
            if #result > 10 and #device_id > 0 then
                device_manager.set_mobile_info(device_id, result)
                device_info = core.json.decode(result)
            end
        end
    end

    return device_info
end

-- ActiveSync 处理函数
local function mobile(ctx, domain_name)
    if ngx.var.uri == "/Microsoft-Server-ActiveSync" then

        local ip = util.get_user_srcip(ctx)

        local full_username = get_username_from_header(ctx)
        -- 登录频率限制函数
        -- login_times_limit(full_username, domain_name)

        local args, err = ngx.req.get_uri_args()
        if err == nil and next(args) then
            local user = args["User"]
            user = basic_auth.get_username_from_ad_account(user, domain_name)

            local username = get_user_from_header(ctx, domain_name) or user
            if username == "" then
                username = user
            end

            local deviceType = args["DeviceType"] or ""
            local device_id = args["DeviceId"] or ""
            local cmd = args["Cmd"] or ""

            device_id = string.lower(device_id)

            local device_info = {
                devicetype = deviceType,
                deviceid = device_id,
                allowtime = ngx.now() * 1000,
                state = 1,
            }

            if #device_id <= 1 then
                ngx.exit(ngx.HTTP_CLOSE)
            end

            -- 保存手机设备的信息到redis中
            get_wbxml_data(device_id)

            local device_str = core.json.encode(device_info)
            local user_state = device_manager.get_user_status(username)

            if user_state == 1 then
                -- 账户被禁用
                ngx.exit(ngx.HTTP_CLOSE)
            else
                if user_state < 0 then
                    -- 账户不存在时，新建账户
                    device_manager.new_user(username)
                end

                local device_status = device_manager.get_device_status(username, device_id)
                ngx.ctx.device_status = device_status

                if device_status < 0 then
                    -- 设备不存在时，新建设备并进入激活流程
                    device_manager.new_device(username, device_id, device_str)
                    device_manager.do_device_active(ctx, username, device_id, device_str)

                    -- 确认指令是否需要过滤
                    local check_cmd_result = check_cmd(cmd, cmd_list.req)
                    if check_cmd_result then
                        -- 设备新建，但未激活时激活设备，如果设备处于激活状态，则暂时不处理
                        ngx.exit(ngx.HTTP_UNAUTHORIZED)
                    end
                    -- 设备已经激活时，不做任务拦截
                elseif device_status == 0 then
                    core.log.info(string.format("time: %s, username: %s, ip: %s, device_id: %s, device_status: %s", ngx.now(),
                            username, ip, device_id, device_status))

                    -- 设备状态为未激活时，进入激活流程
                elseif device_status == 1 then
                    device_manager.do_device_active(ctx, username, device_id, device_str)

                    local check_cmd_result = check_cmd(cmd, cmd_list.req)
                    if check_cmd_result then
                        -- 设备新建，但未激活时激活设备，如果设备处于激活状态，则暂时不处理
                        ngx.exit(ngx.HTTP_CLOSE)
                    end
                else
                    -- 设备锁定或阻止，直接block用户请求
                    ngx.exit(ngx.HTTP_OK)
                end
            end
        end
    end
end

-- 邮件内容返回时，敏感指令过滤
local function filter_response()
    local args, err = ngx.req.get_uri_args()
    if err == nil and next(args) then
        local cmd = args["Cmd"] or "Sync"
        local device_status = tonumber(ngx.ctx.device_status)
        -- 设备未激活时，过滤返回的内容
        if device_status ~= 0 then
            local check_cmd_result = check_cmd(cmd, cmd_list.resp)
            -- 设备新建，但未激活时激活设备，如果设备处于激活状态，则替换返回值，客户端不会出错，但也看不到邮件内容
            if check_cmd_result then
                ngx.arg[1] = ""
            end
        end
    end
end

local _M = {
    mobile = mobile,
    filter_response = filter_response,
}

return _M