int CLua::execfile(const char *filename, bool trusted, bool die_on_fail,
                   bool force)
{
    if (!force && sourced_files.count(filename))
        return 0;

    lua_State *ls = state();
    int err = loadfile(ls, filename, trusted || !managed_vm, die_on_fail);
    lua_call_throttle strangler(this);
    if (!err)
        err = lua_pcall(ls, 0, 0, 0);
    if (!err)
        sourced_files.insert(filename);
    set_error(err);
    if (die_on_fail && !error.empty())
    {
        end(1, false, "Lua execfile error (%s): %s",
            filename, error.c_str());
    }
    return err;
}

Disable lua bytecode loading

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


CLua::~CLua()
{
    // Copy the listener vector, because listeners may remove
    // themselves from the listener list when we notify them of a
    // shutdown.
    const vector<lua_shutdown_listener*> slisteners = shutdown_listeners;
    for (lua_shutdown_listener *listener : slisteners)
        listener->shutdown(*this);
    shutting_down = true;
    if (_state)
        lua_close(_state);
}

Disable lua bytecode loading

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};


void CLua::print_stack()
{
    struct lua_Debug dbg;
    int              i = 0;
    lua_State       *L = state();

    fprintf(stderr, "\n");
    while (lua_getstack(L, i++, &dbg) == 1)
    {
        lua_getinfo(L, "lnuS", &dbg);

        char* file = strrchr(dbg.short_src, '/');
        if (file == nullptr)
            file = dbg.short_src;
        else
            file++;

        fprintf(stderr, "%s, function %s, line %d\n", file,
                dbg.name, dbg.currentline);
    }

    fprintf(stderr, "\n");
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;


lua_text_pattern::lua_text_pattern(const string &_pattern)
    : translated(false), isvalid(true), pattern(_pattern),
      lua_fn_name(new_fn_name())
{
}

Disable lua bytecode loading

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;


lua_datum::lua_datum(CLua &_lua, int stackpos, bool pop)
    : lua(_lua), need_cleanup(true)
{
    // Store the datum in the registry indexed by "this".
    lua_pushvalue(lua, stackpos);
    lua_pushlightuserdata(lua, this);
    // Move the key (this) before the value.
    lua_insert(lua, -2);
    lua_settable(lua, LUA_REGISTRYINDEX);

    if (pop && stackpos < 0)
        lua_pop(lua, -stackpos);

    lua.add_shutdown_listener(this);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


maybe_bool CLua::callmaybefn(const char *fn, const char *params, va_list args)
{
    error.clear();
    lua_State *ls = state();
    if (!ls)
        return MB_MAYBE;

    lua_stack_cleaner clean(ls);

    pushglobal(fn);
    if (!lua_isfunction(ls, -1))
        return MB_MAYBE;

    bool ret = calltopfn(ls, params, args, 1);
    if (!ret)
        return MB_MAYBE;

    return lua_isboolean(ls, -1) ? frombool(lua_toboolean(ls, -1)) : MB_MAYBE;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_text_pattern::~lua_text_pattern()
{
    if (translated && !lua_fn_name.empty())
    {
        lua_State *ls = clua;
        if (ls)
        {
            lua_pushnil(ls);
            clua.setglobal(lua_fn_name.c_str());
        }
    }
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

unsigned int lua_text_pattern::lfndx = 0;


bool lua_text_pattern::translate() const
{
    if (translated || !isvalid)
        return false;

    if (pattern.find("]]") != string::npos || pattern.find("[[") != string::npos)
        return false;

    string textp;
    string luafn;
    const lua_pat_op *currop = nullptr;
    for (string::size_type i = 0; i < pattern.length(); ++i)
    {
        bool match = false;
        for (unsigned p = 0; p < ARRAYSZ(pat_ops); ++p)
        {
            const lua_pat_op &lop = pat_ops[p];
            if (pattern.find(lop.token, i) == i)
            {
                match = true;
                if (lop.pretext && (!currop || currop->posttext))
                {
                    if (currop)
                        textp.erase(0, textp.find_first_not_of(" \r\n\t"));
                    pre_pattern(textp, luafn);
                }

                currop = &lop;
                luafn += lop.luatok;

                i += strlen(lop.token) - 1;

                break;
            }
        }

        if (match)
            continue;

        textp += pattern[i];
    }

    if (currop && currop->posttext)
        post_pattern(textp, luafn);

    luafn = "function " + lua_fn_name + "(text) return " + luafn + " end";

    const_cast<lua_text_pattern *>(this)->translated = true;

    int err = clua.execstring(luafn.c_str(), "stash-search");
    if (err)
    {
        lua_text_pattern *self = const_cast<lua_text_pattern *>(this);
        self->isvalid = self->translated = false;
    }

    return translated;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::getregistry(const char *name)
{
    _getregistry(state(), name);
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}


void CLua::init_lua()
{
    if (_state)
        return;

#ifdef NO_CUSTOM_ALLOCATOR
    // If this is likely to be used as a server, warn the builder.
    // NOTE: #warning doesn't work on MSVC, so this will be fatal there
    // (not that webtiles or dgamelaunch are supported on Windows anyway).
# if defined(USE_TILE_WEB) || defined(DGAMELAUNCH)
#   warning Detected 64-bit Luajit, disabling CLua memory throttling.
# endif
    _state = luaL_newstate();
#else
    // Throttle memory usage in managed (clua) VMs
    _state = managed_vm? lua_newstate(_clua_allocator, this) : luaL_newstate();
#endif
    if (!_state)
        end(1, false, "Unable to create Lua state.");

    lua_stack_cleaner clean(_state);

    lua_atpanic(_state, _clua_panic);

#ifdef CLUA_UNRESTRICTED_LIBS
    // open all libs -- this is not safe for public servers or releases!
    // Intended for people writing bots and the like.
    luaL_openlibs(_state);
#else
    // Selectively load some, but not all Lua core libraries.
    //
    // In Lua 5.1, these library setup calls are not supposed to be called
    // directly from C. If the lua version changes, this may need to be changed:
    // recommended practice is (apparently) checking the lua version's linit.cc
    // and seeing how that does the full library setup.
    //
    // This doesn't seem to *obviously* impact the libraries we use by default,
    // but some of the libraries we don't use will panic if not called
    // correctly; since someone writing a bot (for example) might want to
    // expand this, do things "correctly". The core lua libraries in 5.1 we are
    // not loading are:
    //
    // {LUA_LOADLIBNAME, luaopen_package},    // (require etc)
    // {LUA_IOLIBNAME, luaopen_io},           //
    // {LUA_OSLIBNAME, luaopen_os},
    // {LUA_DBLIBNAME, luaopen_debug},
    const vector<pair<string, lua_CFunction>> lua_core_libs =
    {
        {"", luaopen_base}, // XX: why no name? but this is how linit.cc does it
        {LUA_TABLIBNAME, luaopen_table},
        {LUA_STRLIBNAME, luaopen_string},
        {LUA_MATHLIBNAME, luaopen_math},
    };

    for (auto l : lua_core_libs)
    {
        lua_pushcfunction(_state, l.second);
        lua_pushstring(_state, l.first.c_str());
        lua_call(_state, 1, 0);
    }
#endif

    lua_pushboolean(_state, managed_vm);
    setregistry("lua_vm_is_managed");

    lua_pushlightuserdata(_state, this);
    setregistry("__clua");
}

Disable lua bytecode loading

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

#define NO_CUSTOM_ALLOCATOR

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::setglobal(const char *name)
{
    lua_setglobal(state(), name);
}

Disable lua bytecode loading


void CLua::load_persist()
{
    if (Options.no_save)
        return;
    string persistfile = _get_persist_file();
    if (!file_exists(persistfile))
        return;
    FileLineInput f(persistfile.c_str());
    string script;
    while (!f.eof())
        script += f.get_line() + "\n";
    execstring(script.c_str());
}

Disable lua bytecode loading

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static string _get_persist_file();

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::save_persist()
{
    string persist;
    // We load persist.lua immediately before calling c_save_persist so
    // that we know that it hasn't been overwritten by a player version.
    execfile("dlua/persist.lua", true, true);
    callfn("c_save_persist", ">s", &persist);
    if (Options.no_save)
        return;

    FILE *f;
    const string persistfile = _get_persist_file();

    // Don't create the file if there's no need to do so.
    if (persist.empty() && !file_exists(persistfile))
        return;

    f = fopen_u(persistfile.c_str(), "w");
    if (!f)
    {
        mprf(MSGCH_ERROR, "Couldn't open %s for writing!", persistfile.c_str());
        return;
    }

    fprintf(f, "-- %s %s persistent clua file\n"
               "-- WARNING: This file is entirely auto-generated.\n"
            "\n",
            OUTS(CRAWL), // ok, localizing the game name is not likely
            OUTS(Version::Long)); // nor the version string
    fprintf(f, "%s", persist.c_str());
    fclose(f);
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


maybe_bool CLua::callmbooleanfn(const char *fn, const char *params,
                                va_list args)
{
    error.clear();
    lua_State *ls = state();
    if (!ls)
        return MB_MAYBE;

    lua_stack_cleaner clean(ls);

    pushglobal(fn);
    if (!lua_isfunction(ls, -1))
        return MB_MAYBE;

    bool ret = calltopfn(ls, params, args, 1);
    if (!ret)
        return MB_MAYBE;

    return frombool(lua_toboolean(ls, -1));
}

Disable lua bytecode loading


void lua_text_pattern::post_pattern(string &pat, string &fn) const
{
    pat.erase(0, pat.find_first_not_of(" \t\n\r"));

    fn += " pmatch([[";
    fn += pat;
    fn += "]], text, false) ";

    pat.clear();
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;


static int _clua_loadfile(lua_State *ls)
{
    const char *file = luaL_checkstring(ls, 1);
    if (!file)
        return 0;

    const int err = CLua::loadfile(ls, file, !CLua::is_managed_vm(ls));
    if (err)
    {
        const int place = lua_gettop(ls);
        lua_pushnil(ls);
        lua_insert(ls, place);
        return 2;
    }
    return 1;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_call_throttle::lua_call_throttle(CLua *_lua)
    : lua(_lua)
{
    lua->init_throttle();
    if (!lua->mixed_call_depth++)
        lua_map[lua->state()] = lua;
}

Disable lua bytecode loading

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_datum::~lua_datum()
{
    cleanup();
}

Disable lua bytecode loading

#define NO_CUSTOM_ALLOCATOR

static string _get_persist_file();

static int  _clua_require(lua_State *);

static int  _clua_panic(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

unsigned int lua_text_pattern::lfndx = 0;

static int  _clua_guarded_pcall(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

#define BUGGY_PCALL_ERROR  "667: Malformed response to guarded pcall."

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};


void CLua::add_shutdown_listener(lua_shutdown_listener *listener)
{
    if (find(shutdown_listeners.begin(), shutdown_listeners.end(), listener)
        == shutdown_listeners.end())
    {
        shutdown_listeners.push_back(listener);
    }
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);


static int _clua_guarded_pcall(lua_State *ls)
{
    const int nargs = lua_gettop(ls);
    const int err = lua_pcall(ls, nargs - 1, LUA_MULTRET, 0);

    if (err)
    {
        const char *errs = lua_tostring(ls, 1);
        if (!errs || strstr(errs, BUGGY_SCRIPT_ERROR))
            luaL_error(ls, errs? errs : BUGGY_PCALL_ERROR);
    }

    lua_pushboolean(ls, !err);
    lua_insert(ls, 1);

    return lua_gettop(ls);
}

Disable lua bytecode loading

#define BUGGY_PCALL_ERROR  "667: Malformed response to guarded pcall."

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


maybe_bool CLua::callmbooleanfn(const char *fn, const char *params, ...)
{
    va_list args;
    va_start(args, params);
    maybe_bool r = callmbooleanfn(fn, params, args);
    va_end(args);
    return r;
}

Disable lua bytecode loading

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_datum::lua_datum(const lua_datum &o)
    : lua(o.lua), need_cleanup(true)
{
    set_from(o);
}

Disable lua bytecode loading

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


maybe_bool CLua::callmaybefn(const char *fn, const char *params, ...)
{
    va_list args;
    va_start(args, params);
    maybe_bool r = callmaybefn(fn, params, args);
    va_end(args);
    return r;
}

Disable lua bytecode loading


Disable lua bytecode loading


int CLua::loadbuffer(const char *buf, size_t size, const char *context)
{
    const int err = luaL_loadbuffer(state(), buf, size, context);
    set_error(err, state());
    return err;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


string quote_lua_string(const string &s)
{
    return replace_all_of(replace_all_of(s, "\\", "\\\\"), "\"", "\\\"");
}

Disable lua bytecode loading

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::fnreturns(const char *format, ...)
{
    lua_State *ls = _state;

    if (!format || !ls)
        return;

    va_list args;
    va_start(args, format);
    vfnreturns(format, args);
    va_end(args);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_call_throttle::~lua_call_throttle()
{
    if (!--lua->mixed_call_depth)
        lua_map.erase(lua->state());
}

Disable lua bytecode loading


static int _clua_require(lua_State *ls)
{
    const char *file = luaL_checkstring(ls, 1);
    if (!file)
        return 0;

    CLua &vm(CLua::get_vm(ls));
    if (vm.execfile(file, false, false) != 0)
        luaL_error(ls, vm.error.c_str());

    lua_pushboolean(ls, true);
    return 1;
}

Disable lua bytecode loading


int CLua::execstring(const char *s, const char *context, int nresults)
{
    int err = 0;
    if ((err = loadstring(s, context)))
        return err;

    lua_State *ls = state();
    lua_call_throttle strangler(this);
    err = lua_pcall(ls, 0, nresults, 0);
    set_error(err, ls);
    return err;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


static string _get_persist_file()
{
    return Options.filename + ".persist";
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


int CLua::return_count(lua_State *ls, const char *format)
{
    UNUSED(ls);

    if (!format)
        return 0;

    const char *gs = strchr(format, '>');
    if (gs)
        return strlen(gs + 1);

    const char *cs = strchr(format, ':');
    if (cs && isdigit(*format))
    {
        char *es = nullptr;
        int ci = strtol(format, &es, 10);
        // We're capping return at 10 here, which is arbitrary, but avoids
        // blowing the stack.
        if (ci < 0)
            ci = 0;
        else if (ci > 10)
            ci = 10;
        return ci;
    }
    return 0;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


string lua_text_pattern::new_fn_name()
{
    return make_stringf("__ch_stash_search_%u", lfndx++);
}

Disable lua bytecode loading

static string _get_persist_file();

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void lua_datum::set_from(const lua_datum &o)
{
    lua_pushlightuserdata(lua, this);
    o.push();
    lua_settable(lua, LUA_REGISTRYINDEX);
    lua.add_shutdown_listener(this);
    need_cleanup = true;
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::vfnreturns(const char *format, va_list args)
{
    lua_State *ls = _state;
    int nrets = return_count(ls, format);
    int sp = -nrets - 1;

    const char *gs = strchr(format, '>');
    if (gs)
        format = gs + 1;
    else if ((gs = strchr(format, ':')))
        format = gs + 1;

    for (const char *run = format; *run; ++run)
    {
        char argtype = *run;
        ++sp;
        switch (argtype)
        {
        case 'u':
            if (lua_islightuserdata(ls, sp))
                *(va_arg(args, void**)) = lua_touserdata(ls, sp);
            break;
        case 'd':
            if (lua_isnumber(ls, sp))
                *(va_arg(args, int*)) = luaL_safe_checkint(ls, sp);
            break;
        case 'b':
            *(va_arg(args, bool *)) = lua_toboolean(ls, sp);
            break;
        case 's':
            {
                const char *s = lua_tostring(ls, sp);
                if (s)
                    *(va_arg(args, string *)) = s;
                break;
            }
        default:
            break;
        }

    }
    // Pop args off the stack
    lua_pop(ls, nrets);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;


void CLua::setregistry(const char *name)
{
    lua_pushstring(state(), name);
    // Slide name round before the value
    lua_insert(state(), -2);
    lua_settable(state(), LUA_REGISTRYINDEX);
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}


bool lua_text_pattern::valid() const
{
    return translated? isvalid : translate();
}

Disable lua bytecode loading

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


int CLua::loadstring(const char *s, const char *context)
{
    return loadbuffer(s, strlen(s), context);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


lua_State *CLua::state()
{
    if (!_state)
        init_lua();
    return _state;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


CLua &CLua::get_vm(lua_State *ls)
{
    lua_stack_cleaner clean(ls);
    _getregistry(ls, "__clua");
    CLua *vm = clua_get_lightuserdata<CLua>(ls, -1);
    if (!vm)
        luaL_error(ls, "Could not find matching clua for lua state");
    return *vm;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


bool lua_datum::is_udata() const
{
    LUA_CHECK_TYPE(lua_isuserdata);
}

Disable lua bytecode loading


void CLua::set_error(int err, lua_State *ls)
{
    if (!err)
    {
        error.clear();
        return;
    }
    if (!ls && !(ls = _state))
    {
        error = "<LUA not initialised>";
        return;
    }
    const char *serr = lua_tostring(ls, -1);
    lua_pop(ls, 1);
    error = serr? serr : "<Unknown error>";
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


bool CLua::callfn(const char *fn, int nargs, int nret)
{
    error.clear();
    lua_State *ls = state();
    if (!ls)
        return false;

    // If a function is not provided on the stack, get the named function.
    if (fn)
    {
        pushglobal(fn);
        if (!lua_isfunction(ls, -1))
        {
            lua_settop(ls, -nargs - 2);
            return false;
        }

        // Slide the function in front of its args and call it.
        if (nargs)
            lua_insert(ls, -nargs - 1);
    }

    lua_call_throttle strangler(this);
    int err = lua_pcall(ls, nargs, nret, 0);
    set_error(err, ls);
    return !err;
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


CLua *lua_call_throttle::find_clua(lua_State *ls)
{
    return lookup(lua_map, ls, nullptr);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


bool CLua::is_managed_vm(lua_State *ls)
{
    lua_stack_cleaner clean(ls);
    lua_pushstring(ls, "lua_vm_is_managed");
    lua_gettable(ls, LUA_REGISTRYINDEX);
    return lua_toboolean(ls, -1);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


static int lua_loadstring(lua_State *ls)
{
    const auto lua = luaL_checkstring(ls, 1);
    if (lua[0] == 0x1b)
        abort();
    lua_settop(ls, 0);
    if (luaL_loadstring(ls, lua))
    {
        lua_pushnil(ls);
        lua_insert(ls, 1);
    }
    return lua_gettop(ls);
}

Disable lua bytecode loading

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::pushglobal(const string &name)
{
    vector<string> pieces = split_string(".", name);
    lua_State *ls(state());

    if (pieces.empty())
        lua_pushnil(ls);

    for (unsigned i = 0, size = pieces.size(); i < size; ++i)
    {
        if (!i)
            lua_getglobal(ls, pieces[i].c_str());
        else
        {
            if (lua_istable(ls, -1))
            {
                lua_pushstring(ls, pieces[i].c_str());
                lua_gettable(ls, -2);
                // Swap the value we just found with the table itself.
                lua_insert(ls, -2);
                // And remove the table.
                lua_pop(ls, 1);
            }
            else
            {
                // We expected a table here, but got something else. Fail.
                lua_pop(ls, 1);
                lua_pushnil(ls);
                break;
            }
        }
    }
}

Disable lua bytecode loading


bool lua_datum::is_function() const
{
    LUA_CHECK_TYPE(lua_isfunction);
}

Disable lua bytecode loading

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


const lua_datum &lua_datum::operator = (const lua_datum &o)
{
    if (this != &o)
    {
        cleanup();
        set_from(o);
    }
    return *this;
}

Disable lua bytecode loading


bool CLua::is_path_safe(string s, bool trusted)
{
    lowercase(s);
    return s.find("..") == string::npos && shell_safe(s.c_str())
           // loading dlua stuff would spew tons of error messages
           && (trusted || s.find("dlua") != 0);
}

Disable lua bytecode loading

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


void CLua::init_throttle()
{
    if (!managed_vm)
        return;

    if (!crawl_state.throttle)
        return;

    if (throttle_unit_lines <= 0)
        throttle_unit_lines = 500;

    if (throttle_sleep_start < 1)
        throttle_sleep_start = 1;

    if (throttle_sleep_end < throttle_sleep_start)
        throttle_sleep_end = throttle_sleep_start;

    if (!mixed_call_depth)
    {
        lua_sethook(_state, _clua_throttle_hook,
                    LUA_MASKCOUNT, throttle_unit_lines);
        throttle_sleep_ms = 0;
        n_throttle_sleeps = 0;
    }
}

Disable lua bytecode loading

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


static void _clua_throttle_hook(lua_State *ls, lua_Debug *dbg)
{
    UNUSED(dbg);

    CLua *lua = lua_call_throttle::find_clua(ls);

    // Co-routines can create a new Lua state; in such cases, we must
    // fudge it.
    if (!lua)
        lua = &clua;

    if (lua)
    {
        if (!lua->throttle_sleep_ms)
            lua->throttle_sleep_ms = lua->throttle_sleep_start;
        else if (lua->throttle_sleep_ms < lua->throttle_sleep_end)
            lua->throttle_sleep_ms *= 2;

        ++lua->n_throttle_sleeps;

        delay(lua->throttle_sleep_ms);

        // Try to kill the annoying script.
        if (lua->n_throttle_sleeps > CLua::MAX_THROTTLE_SLEEPS)
        {
            lua->n_throttle_sleeps = CLua::MAX_THROTTLE_SLEEPS;
            luaL_error(ls, BUGGY_SCRIPT_ERROR);
        }
    }
}

Disable lua bytecode loading

#define BUGGY_SCRIPT_ERROR "666: Killing badly-behaved Lua script."

static int  _clua_panic(lua_State *);

static void _clua_throttle_hook(lua_State *, lua_Debug *);

static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize);

static int  _clua_guarded_pcall(lua_State *);

static int  _clua_require(lua_State *);

static int  _clua_dofile(lua_State *);

static int  _clua_loadfile(lua_State *);

static string _get_persist_file();

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

lua_call_throttle::lua_clua_map lua_call_throttle::lua_map;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


pattern_match lua_text_pattern::match_location(const string &s) const
{
    // lua_text_pattern is only used if a special non-regex op is detected (^F
    // for "armour && ego", for instance), and in those situations, it's
    // unclear what exactly to use for the matched text here (especially in
    // more complicated expressions that include things like <<>>, !!, etc).
    return matches(s)
        ? pattern_match::succeeded(s)
        : pattern_match::failed(s);
}

Disable lua bytecode loading

static string _get_persist_file();

struct lua_pat_op
{
    const char *token;
    const char *luatok;

    bool pretext;       // Does this follow a pattern?
    bool posttext;      // Is this followed by a pattern?
}

static lua_pat_op pat_ops[] =
{
    { "<<", " ( ",   false, true },
    { ">>", " ) ",   true,  false },
    { "!!", " not ", false, true },
    { "==", " == ",  true,  true },
    { "^^", " ~= ",  true,  true },
    { "&&", " and ", true,  true },
    { "||", " or ",  true,  true },
};

unsigned int lua_text_pattern::lfndx = 0;

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


FILE *CLua::CLuaSave::get_file()
{
    if (!handle)
        handle = fopen_u(filename, "w");

    return handle;
}

Disable lua bytecode loading

#define LUA_CHECK_TYPE(check) \
    lua_stack_cleaner clean(lua);                               \
    push();                                                     \
    return check(lua, -1)


GF_Err abst_box_read(GF_Box *s, GF_BitStream *bs)
{
	GF_AdobeBootstrapInfoBox *ptr = (GF_AdobeBootstrapInfoBox *)s;
	int i;
	u32 tmp_strsize;
	char *tmp_str;
	Bool zfound=GF_FALSE;
	GF_Err e = GF_OK;

	ISOM_DECREASE_SIZE(ptr, 25)
	ptr->bootstrapinfo_version = gf_bs_read_u32(bs);
	ptr->profile = gf_bs_read_int(bs, 2);
	ptr->live = gf_bs_read_int(bs, 1);
	ptr->update = gf_bs_read_int(bs, 1);
	ptr->reserved = gf_bs_read_int(bs, 4);
	ptr->time_scale = gf_bs_read_u32(bs);
	ptr->current_media_time = gf_bs_read_u64(bs);
	ptr->smpte_time_code_offset = gf_bs_read_u64(bs);

	i=0;
	if (ptr->size<8) return GF_ISOM_INVALID_FILE;
	tmp_strsize =(u32)ptr->size;
	tmp_str = gf_malloc(sizeof(char)*tmp_strsize);
	if (!tmp_str) return GF_OUT_OF_MEM;
	memset(tmp_str, 0, sizeof(char)*tmp_strsize);

	while (tmp_strsize) {
		ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
		tmp_str[i] = gf_bs_read_u8(bs);
		tmp_strsize--;
		if (!tmp_str[i]) {
			zfound = GF_TRUE;
			break;
		}
		i++;
	}
	if (!zfound) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}
	if (i) {
		ptr->movie_identifier = gf_strdup(tmp_str);
	}

	ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
	ptr->server_entry_count = gf_bs_read_u8(bs);
	for (i=0; i<ptr->server_entry_count; i++) {
		int j=0;
		zfound = GF_FALSE;
		tmp_strsize=(u32)ptr->size;
		while (tmp_strsize) {
			ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
			tmp_str[j] = gf_bs_read_u8(bs);
			tmp_strsize--;
			if (!tmp_str[j]) {
				zfound = GF_TRUE;
				break;
			}
			j++;
		}
		if (!zfound) {
			e = GF_ISOM_INVALID_FILE;
			goto exit;
		}
		if (j) {
			gf_list_insert(ptr->server_entry_table, gf_strdup(tmp_str), i);
		}
	}
	if (ptr->server_entry_count != gf_list_count(ptr->server_entry_table)) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

	ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
	ptr->quality_entry_count = gf_bs_read_u8(bs);
	for (i=0; i<ptr->quality_entry_count; i++) {
		int j=0;
		zfound = GF_FALSE;
		tmp_strsize=(u32)ptr->size;
		while (tmp_strsize) {
			ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
			tmp_str[j] = gf_bs_read_u8(bs);
			tmp_strsize--;
			if (!tmp_str[j]) {
				zfound = GF_TRUE;
				break;
			}
			j++;
		}

		if (!zfound) {
			e = GF_ISOM_INVALID_FILE;
			goto exit;
		}
		if (j) {
			gf_list_insert(ptr->quality_entry_table, gf_strdup(tmp_str), i);
		}
	}
	if (ptr->quality_entry_count != gf_list_count(ptr->quality_entry_table)) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

	i=0;
	tmp_strsize=(u32)ptr->size;
	zfound = GF_FALSE;
	while (tmp_strsize) {
		ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
		tmp_str[i] = gf_bs_read_u8(bs);
		tmp_strsize--;
		if (!tmp_str[i]) {
			zfound = GF_TRUE;
			break;
		}
		i++;
	}
	if (!zfound) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

	if (i) {
		ptr->drm_data = gf_strdup(tmp_str);
	}

	i=0;
	tmp_strsize=(u32)ptr->size;
	zfound = GF_FALSE;
	while (tmp_strsize) {
		ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
		tmp_str[i] = gf_bs_read_u8(bs);
		tmp_strsize--;
		if (!tmp_str[i]) {
			zfound = GF_TRUE;
			break;
		}
		i++;
	}
	if (!zfound) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

	if (i) {
		ptr->meta_data = gf_strdup(tmp_str);
	}

	ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
	ptr->segment_run_table_count = gf_bs_read_u8(bs);
	for (i=0; i<ptr->segment_run_table_count; i++) {
		GF_AdobeSegmentRunTableBox *asrt = NULL;
		e = gf_isom_box_parse((GF_Box **)&asrt, bs);
		if (e) {
			if (asrt) gf_isom_box_del((GF_Box*)asrt);
			goto exit;
		}
		gf_list_add(ptr->segment_run_table_entries, asrt);
	}
	if (ptr->segment_run_table_count != gf_list_count(ptr->segment_run_table_entries)) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

	ISOM_DECREASE_SIZE_GOTO_EXIT(ptr, 1)
	ptr->fragment_run_table_count = gf_bs_read_u8(bs);
	for (i=0; i<ptr->fragment_run_table_count; i++) {
		GF_AdobeFragmentRunTableBox *afrt = NULL;
		e = gf_isom_box_parse((GF_Box **)&afrt, bs);
		if (e) {
			if (afrt) gf_isom_box_del((GF_Box*)afrt);
			goto exit;
		}
		gf_list_add(ptr->fragment_run_table_entries, afrt);
	}
	if (ptr->fragment_run_table_count != gf_list_count(ptr->fragment_run_table_entries)) {
		e = GF_ISOM_INVALID_FILE;
		goto exit;
	}

exit:
	gf_free(tmp_str);
	return e;
}

fixed #1753

/* No explicit global definitions found. */


void afra_box_del(GF_Box *s)
{
	GF_AdobeFragRandomAccessBox *ptr = (GF_AdobeFragRandomAccessBox *)s;
	if (ptr == NULL) return;

	while (gf_list_count(ptr->local_access_entries)) {
		gf_free(gf_list_get(ptr->local_access_entries, 0));
		gf_list_rem(ptr->local_access_entries, 0);
	}
	gf_list_del(ptr->local_access_entries);

	while (gf_list_count(ptr->global_access_entries)) {
		gf_free(gf_list_get(ptr->global_access_entries, 0));
		gf_list_rem(ptr->global_access_entries, 0);
	}
	gf_list_del(ptr->global_access_entries);

	gf_free(ptr);
}

fixed #1753


GF_Err afrt_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	unsigned int i;
	GF_AdobeFragmentRunTableBox *ptr = (GF_AdobeFragmentRunTableBox *)s;

	e = gf_isom_full_box_write(s, bs);
	if (e) return e;

	gf_bs_write_u32(bs, ptr->timescale);
	gf_bs_write_u8(bs, ptr->quality_entry_count);
	for (i=0; i<ptr->quality_entry_count; i++) {
		char *str = (char*)gf_list_get(ptr->quality_segment_url_modifiers, i);
		gf_bs_write_data(bs, str, (u32)strlen(str) + 1);
	}

	gf_bs_write_u32(bs, ptr->fragment_run_entry_count);
	for (i=0; i<ptr->fragment_run_entry_count; i++) {
		GF_AdobeFragmentRunEntry *fre = (GF_AdobeFragmentRunEntry *)gf_list_get(ptr->fragment_run_entry_table, i);
		gf_bs_write_u32(bs, fre->first_fragment);
		gf_bs_write_u64(bs, fre->first_fragment_timestamp);
		gf_bs_write_u32(bs, fre->fragment_duration);
		if (!fre->fragment_duration)
			gf_bs_write_u8(bs, fre->discontinuity_indicator);
	}

	return GF_OK;
}

fixed #1753


GF_Err afrt_box_read(GF_Box *s, GF_BitStream *bs)
{
	unsigned int i;
	GF_AdobeFragmentRunTableBox *ptr = (GF_AdobeFragmentRunTableBox *)s;

	ISOM_DECREASE_SIZE(ptr, 5)
	ptr->timescale = gf_bs_read_u32(bs);

	ptr->quality_entry_count = gf_bs_read_u8(bs);
	if (ptr->size < ptr->quality_entry_count)
		return GF_ISOM_INVALID_FILE;

	for (i=0; i<ptr->quality_entry_count; i++) {
		int j=0;
		u32 tmp_strsize=(u32)ptr->size-8;
		char *tmp_str = (char*) gf_malloc(tmp_strsize);
		if (!tmp_str) return GF_OUT_OF_MEM;
		while (tmp_strsize) {
			tmp_str[j] = gf_bs_read_u8(bs);
			tmp_strsize--;
			if (!tmp_str[j])
				break;
			j++;
		}
		ISOM_DECREASE_SIZE(ptr, j)
		gf_list_insert(ptr->quality_segment_url_modifiers, tmp_str, i);
	}

	ptr->fragment_run_entry_count = gf_bs_read_u32(bs);
	if (ptr->size < ptr->fragment_run_entry_count*16)
		return GF_ISOM_INVALID_FILE;
	for (i=0; i<ptr->fragment_run_entry_count; i++) {
		GF_AdobeFragmentRunEntry *fre = gf_malloc(sizeof(GF_AdobeFragmentRunEntry));
		if (!fre) return GF_ISOM_INVALID_FILE;
		ISOM_DECREASE_SIZE(ptr, 16)
		fre->first_fragment = gf_bs_read_u32(bs);
		fre->first_fragment_timestamp = gf_bs_read_u64(bs);
		fre->fragment_duration = gf_bs_read_u32(bs);
		if (!fre->fragment_duration) {
			ISOM_DECREASE_SIZE(ptr, 1)
			fre->discontinuity_indicator = gf_bs_read_u8(bs);
		}
		gf_list_insert(ptr->fragment_run_entry_table, fre, i);
	}

	return GF_OK;
}

fixed #1753


GF_Err afrt_box_size(GF_Box *s)
{
	u32 i;
	GF_AdobeFragmentRunTableBox *ptr = (GF_AdobeFragmentRunTableBox *)s;

	s->size += 5;

	for (i=0; i<ptr->quality_entry_count; i++)
		s->size += strlen(gf_list_get(ptr->quality_segment_url_modifiers, i)) + 1;

	s->size += 4;

	for (i=0; i<ptr->fragment_run_entry_count; i++) {
		GF_AdobeFragmentRunEntry *fre = (GF_AdobeFragmentRunEntry *)gf_list_get(ptr->fragment_run_entry_table, i);
		if (fre->fragment_duration)
			s->size += 16;
		else
			s->size += 17;
	}

	return GF_OK;
}

fixed #1753


GF_Err asrt_box_read(GF_Box *s, GF_BitStream *bs)
{
	unsigned int i;
	GF_AdobeSegmentRunTableBox *ptr = (GF_AdobeSegmentRunTableBox *)s;

	ISOM_DECREASE_SIZE(ptr, 1)
	ptr->quality_entry_count = gf_bs_read_u8(bs);
	if (ptr->size < ptr->quality_entry_count)
		return GF_ISOM_INVALID_FILE;

	for (i=0; i<ptr->quality_entry_count; i++) {
		int j=0;
		u32 tmp_strsize=(u32)ptr->size;
		char *tmp_str = (char*) gf_malloc(tmp_strsize);
		if (!tmp_str) return GF_OUT_OF_MEM;
		while (tmp_strsize) {
			tmp_str[j] = gf_bs_read_u8(bs);
			tmp_strsize--;
			if (!tmp_str[j])
				break;
			j++;
		}
		ISOM_DECREASE_SIZE(ptr, j)
		gf_list_insert(ptr->quality_segment_url_modifiers, tmp_str, i);
	}

	ISOM_DECREASE_SIZE(ptr, 4)
	ptr->segment_run_entry_count = gf_bs_read_u32(bs);
	if (ptr->size < ptr->segment_run_entry_count*8)
		return GF_ISOM_INVALID_FILE;

	for (i=0; i<ptr->segment_run_entry_count; i++) {
		GF_AdobeSegmentRunEntry *sre = gf_malloc(sizeof(GF_AdobeSegmentRunEntry));
		if (!sre) return GF_OUT_OF_MEM;
		ISOM_DECREASE_SIZE(ptr, 8)
		sre->first_segment = gf_bs_read_u32(bs);
		sre->fragment_per_segment = gf_bs_read_u32(bs);
		gf_list_insert(ptr->segment_run_entry_table, sre, i);
	}

	return GF_OK;
}

fixed #1753


GF_Err abst_box_size(GF_Box *s)
{
	GF_Err e;
	u32 i;
	GF_AdobeBootstrapInfoBox *ptr = (GF_AdobeBootstrapInfoBox *)s;

	s->size += 25
	           + (ptr->movie_identifier ? (strlen(ptr->movie_identifier) + 1) : 1)
	           + 1;

	for (i=0; i<ptr->server_entry_count; i++)
		s->size += strlen(gf_list_get(ptr->server_entry_table, i)) + 1;

	s->size += 1;

	for (i=0; i<ptr->quality_entry_count; i++)
		s->size += strlen(gf_list_get(ptr->quality_entry_table, i)) + 1;

	s->size += (ptr->drm_data ? (strlen(ptr->drm_data) + 1) : 1)
	           + (ptr->meta_data ? (strlen(ptr->meta_data) + 1) : 1)
	           + 1;

	for (i=0; i<ptr->segment_run_table_count; i++) {
		GF_Box *box = (GF_Box *)gf_list_get(ptr->segment_run_table_entries, i);
		e = gf_isom_box_size(box);
		if (e) return e;
		s->size += box->size;
	}

	s->size += 1;
	for (i=0; i<ptr->fragment_run_table_count; i++) {
		GF_Box *box = (GF_Box *)gf_list_get(ptr->fragment_run_table_entries, i);
		e = gf_isom_box_size(box);
		if (e) return e;
		s->size += box->size;
	}
	return GF_OK;
}

fixed #1753


GF_Box *afra_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_AdobeFragRandomAccessBox, GF_ISOM_BOX_TYPE_AFRA);
	tmp->local_access_entries = gf_list_new();
	tmp->global_access_entries = gf_list_new();
	return (GF_Box *)tmp;
}

fixed #1753


void asrt_box_del(GF_Box *s)
{
	GF_AdobeSegmentRunTableBox *ptr = (GF_AdobeSegmentRunTableBox *)s;
	if (ptr == NULL) return;

	while (gf_list_count(ptr->quality_segment_url_modifiers)) {
		gf_free(gf_list_get(ptr->quality_segment_url_modifiers, 0));
		gf_list_rem(ptr->quality_segment_url_modifiers, 0);
	}
	gf_list_del(ptr->quality_segment_url_modifiers);

	while (gf_list_count(ptr->segment_run_entry_table)) {
		gf_free(gf_list_get(ptr->segment_run_entry_table, 0));
		gf_list_rem(ptr->segment_run_entry_table, 0);
	}
	gf_list_del(ptr->segment_run_entry_table);

	gf_free(ptr);
}

fixed #1753


GF_Err asrt_box_size(GF_Box *s)
{
	int i;
	GF_AdobeSegmentRunTableBox *ptr = (GF_AdobeSegmentRunTableBox *)s;

	s->size += 5;

	for (i=0; i<ptr->quality_entry_count; i++)
		s->size += strlen(gf_list_get(ptr->quality_segment_url_modifiers, i)) + 1;

	s->size += ptr->segment_run_entry_count * sizeof(GF_AdobeSegmentRunEntry);

	return GF_OK;
}

fixed #1753


GF_Err afra_box_read(GF_Box *s, GF_BitStream *bs)
{
	unsigned int i;
	GF_AdobeFragRandomAccessBox *ptr = (GF_AdobeFragRandomAccessBox *)s;

	ISOM_DECREASE_SIZE(ptr, 9)
	ptr->long_ids = gf_bs_read_int(bs, 1);
	ptr->long_offsets = gf_bs_read_int(bs, 1);
	ptr->global_entries = gf_bs_read_int(bs, 1);
	ptr->reserved = gf_bs_read_int(bs, 5);
	ptr->time_scale = gf_bs_read_u32(bs);

	ptr->entry_count = gf_bs_read_u32(bs);
	if (ptr->size < ptr->entry_count * (ptr->long_offsets ? 16 : 12))
		return GF_ISOM_INVALID_FILE;

	for (i=0; i<ptr->entry_count; i++) {
		GF_AfraEntry *ae = gf_malloc(sizeof(GF_AfraEntry));
		if (!ae) return GF_OUT_OF_MEM;

		ISOM_DECREASE_SIZE(ptr, 8)
		ae->time = gf_bs_read_u64(bs);
		if (ptr->long_offsets) {
			ISOM_DECREASE_SIZE(ptr, 8)
			ae->offset = gf_bs_read_u64(bs);
		} else {
			ISOM_DECREASE_SIZE(ptr, 4)
			ae->offset = gf_bs_read_u32(bs);
		}

		gf_list_insert(ptr->local_access_entries, ae, i);
	}

	if (ptr->global_entries) {
		ISOM_DECREASE_SIZE(ptr, 4)
		ptr->global_entry_count = gf_bs_read_u32(bs);
		for (i=0; i<ptr->global_entry_count; i++) {
			GF_GlobalAfraEntry *ae = gf_malloc(sizeof(GF_GlobalAfraEntry));
			if (!ae) return GF_OUT_OF_MEM;
			ISOM_DECREASE_SIZE(ptr, 8)
			ae->time = gf_bs_read_u64(bs);
			if (ptr->long_ids) {
				ISOM_DECREASE_SIZE(ptr, 8)
				ae->segment = gf_bs_read_u32(bs);
				ae->fragment = gf_bs_read_u32(bs);
			} else {
				ISOM_DECREASE_SIZE(ptr, 4)
				ae->segment = gf_bs_read_u16(bs);
				ae->fragment = gf_bs_read_u16(bs);
			}
			if (ptr->long_offsets) {
				ISOM_DECREASE_SIZE(ptr, 16)
				ae->afra_offset = gf_bs_read_u64(bs);
				ae->offset_from_afra = gf_bs_read_u64(bs);
			} else {
				ISOM_DECREASE_SIZE(ptr, 8)
				ae->afra_offset = gf_bs_read_u32(bs);
				ae->offset_from_afra = gf_bs_read_u32(bs);
			}

			gf_list_insert(ptr->global_access_entries, ae, i);
		}
	}

	return GF_OK;
}

fixed #1753


GF_Err abst_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	unsigned int i;
	GF_AdobeBootstrapInfoBox *ptr = (GF_AdobeBootstrapInfoBox *)s;

	e = gf_isom_full_box_write(s, bs);
	if (e) return e;

	gf_bs_write_u32(bs, ptr->bootstrapinfo_version);
	gf_bs_write_int(bs, ptr->profile, 2);
	gf_bs_write_int(bs, ptr->live, 1);
	gf_bs_write_int(bs, ptr->update, 1);
	gf_bs_write_int(bs, ptr->reserved, 4);
	gf_bs_write_u32(bs, ptr->time_scale);
	gf_bs_write_u64(bs, ptr->current_media_time);
	gf_bs_write_u64(bs, ptr->smpte_time_code_offset);
	if (ptr->movie_identifier)
		gf_bs_write_data(bs, ptr->movie_identifier, (u32)strlen(ptr->movie_identifier) + 1);
	else
		gf_bs_write_u8(bs, 0);

	gf_bs_write_u8(bs, ptr->server_entry_count);
	for (i=0; i<ptr->server_entry_count; i++) {
		char *str = (char*)gf_list_get(ptr->server_entry_table, i);
		gf_bs_write_data(bs, str, (u32)strlen(str) + 1);
	}

	gf_bs_write_u8(bs, ptr->quality_entry_count);
	for (i=0; i<ptr->quality_entry_count; i++) {
		char *str = (char*)gf_list_get(ptr->quality_entry_table, i);
		gf_bs_write_data(bs, str, (u32)strlen(str) + 1);
	}

	if (ptr->drm_data)
		gf_bs_write_data(bs, ptr->drm_data, (u32)strlen(ptr->drm_data) + 1);
	else
		gf_bs_write_u8(bs, 0);

	if (ptr->meta_data)
		gf_bs_write_data(bs, ptr->meta_data, (u32)strlen(ptr->meta_data) + 1);
	else
		gf_bs_write_u8(bs, 0);

	gf_bs_write_u8(bs, ptr->segment_run_table_count);
	for (i=0; i<ptr->segment_run_table_count; i++) {
		e = gf_isom_box_write((GF_Box *)gf_list_get(ptr->segment_run_table_entries, i), bs);
		if (e) return e;
	}

	gf_bs_write_u8(bs, ptr->fragment_run_table_count);
	for (i=0; i<ptr->fragment_run_table_count; i++) {
		e = gf_isom_box_write((GF_Box *)gf_list_get(ptr->fragment_run_table_entries, i), bs);
		if (e) return e;
	}

	return GF_OK;
}

fixed #1753


GF_Box *asrt_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_AdobeSegmentRunTableBox, GF_ISOM_BOX_TYPE_ASRT);
	tmp->quality_segment_url_modifiers = gf_list_new();
	tmp->segment_run_entry_table = gf_list_new();
	return (GF_Box *)tmp;
}

fixed #1753


GF_Box *afrt_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_AdobeFragmentRunTableBox, GF_ISOM_BOX_TYPE_AFRT);
	tmp->quality_segment_url_modifiers = gf_list_new();
	tmp->fragment_run_entry_table = gf_list_new();
	return (GF_Box *)tmp;
}

fixed #1753


GF_Err afra_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	unsigned int i;
	GF_AdobeFragRandomAccessBox *ptr = (GF_AdobeFragRandomAccessBox *)s;

	e = gf_isom_full_box_write(s, bs);
	if (e) return e;

	gf_bs_write_int(bs, ptr->long_ids, 1);
	gf_bs_write_int(bs, ptr->long_offsets, 1);
	gf_bs_write_int(bs, ptr->global_entries, 1);
	gf_bs_write_int(bs, 0, 5);
	gf_bs_write_u32(bs, ptr->time_scale);

	gf_bs_write_u32(bs, ptr->entry_count);
	for (i=0; i<ptr->entry_count; i++) {
		GF_AfraEntry *ae = (GF_AfraEntry *)gf_list_get(ptr->local_access_entries, i);
		gf_bs_write_u64(bs, ae->time);
		if (ptr->long_offsets)
			gf_bs_write_u64(bs, ae->offset);
		else
			gf_bs_write_u32(bs, (u32)ae->offset);
	}

	if (ptr->global_entries) {
		gf_bs_write_u32(bs, ptr->global_entry_count);
		for (i=0; i<ptr->global_entry_count; i++) {
			GF_GlobalAfraEntry *gae = (GF_GlobalAfraEntry *)gf_list_get(ptr->global_access_entries, i);
			gf_bs_write_u64(bs, gae->time);
			if (ptr->long_ids) {
				gf_bs_write_u32(bs, gae->segment);
				gf_bs_write_u32(bs, gae->fragment);
			} else {
				gf_bs_write_u16(bs, (u16)gae->segment);
				gf_bs_write_u16(bs, (u16)gae->fragment);
			}
			if (ptr->long_offsets) {
				gf_bs_write_u64(bs, gae->afra_offset);
				gf_bs_write_u64(bs, gae->offset_from_afra);
			} else {
				gf_bs_write_u32(bs, (u32)gae->afra_offset);
				gf_bs_write_u32(bs, (u32)gae->offset_from_afra);
			}
		}
	}

	return GF_OK;
}

fixed #1753


GF_Err afra_box_size(GF_Box *s)
{
	GF_AdobeFragRandomAccessBox *ptr = (GF_AdobeFragRandomAccessBox *)s;

	s->size += 9
	           + ptr->entry_count * (ptr->long_offsets ? 16 : 12)
	           + (ptr->global_entries ? 4 + ptr->global_entry_count * (4 + (ptr->long_offsets ? 16 : 8) + (ptr->long_ids ? 8 : 4)) : 0);

	return GF_OK;
}

fixed #1753


GF_Err asrt_box_write(GF_Box *s, GF_BitStream *bs)
{
	GF_Err e;
	unsigned int i;
	GF_AdobeSegmentRunTableBox *ptr = (GF_AdobeSegmentRunTableBox *)s;

	e = gf_isom_full_box_write(s, bs);
	if (e) return e;

	gf_bs_write_u8(bs, ptr->quality_entry_count);
	for (i=0; i<ptr->quality_entry_count; i++) {
		char *str = (char*)gf_list_get(ptr->quality_segment_url_modifiers, i);
		gf_bs_write_data(bs, str, (u32)strlen(str) + 1);
	}

	gf_bs_write_u32(bs, ptr->segment_run_entry_count);
	for (i=0; i<ptr->segment_run_entry_count; i++) {
		GF_AdobeSegmentRunEntry *sre = (GF_AdobeSegmentRunEntry *)gf_list_get(ptr->segment_run_entry_table, i);
		gf_bs_write_u32(bs, sre->first_segment);
		gf_bs_write_u32(bs, sre->fragment_per_segment);
	}

	return GF_OK;
}

fixed #1753


void abst_box_del(GF_Box *s)
{
	GF_AdobeBootstrapInfoBox *ptr = (GF_AdobeBootstrapInfoBox *)s;
	if (ptr == NULL) return;

	if (ptr->movie_identifier)
		gf_free(ptr->movie_identifier);
	if (ptr->drm_data)
		gf_free(ptr->drm_data);
	if (ptr->meta_data)
		gf_free(ptr->meta_data);

	while (gf_list_count(ptr->server_entry_table)) {
		gf_free(gf_list_get(ptr->server_entry_table, 0));
		gf_list_rem(ptr->server_entry_table, 0);
	}
	gf_list_del(ptr->server_entry_table);

	while (gf_list_count(ptr->quality_entry_table)) {
		gf_free(gf_list_get(ptr->quality_entry_table, 0));
		gf_list_rem(ptr->quality_entry_table, 0);
	}
	gf_list_del(ptr->quality_entry_table);

	while (gf_list_count(ptr->segment_run_table_entries)) {
		gf_isom_box_del((GF_Box *)gf_list_get(ptr->segment_run_table_entries, 0));
		gf_list_rem(ptr->segment_run_table_entries, 0);
	}
	gf_list_del(ptr->segment_run_table_entries);

	while (gf_list_count(ptr->fragment_run_table_entries)) {
		gf_isom_box_del((GF_Box *)gf_list_get(ptr->fragment_run_table_entries, 0));
		gf_list_rem(ptr->fragment_run_table_entries, 0);
	}
	gf_list_del(ptr->fragment_run_table_entries);

	gf_free(ptr);
}

fixed #1753


GF_Box *abst_box_new()
{
	ISOM_DECL_BOX_ALLOC(GF_AdobeBootstrapInfoBox, GF_ISOM_BOX_TYPE_ABST);
	tmp->server_entry_table = gf_list_new();
	tmp->quality_entry_table = gf_list_new();
	tmp->segment_run_table_entries = gf_list_new();
	tmp->fragment_run_table_entries = gf_list_new();
	return (GF_Box *)tmp;
}

fixed #1753


void afrt_box_del(GF_Box *s)
{
	GF_AdobeFragmentRunTableBox *ptr = (GF_AdobeFragmentRunTableBox *)s;
	if (ptr == NULL) return;

	while (gf_list_count(ptr->quality_segment_url_modifiers)) {
		gf_free(gf_list_get(ptr->quality_segment_url_modifiers, 0));
		gf_list_rem(ptr->quality_segment_url_modifiers, 0);
	}
	gf_list_del(ptr->quality_segment_url_modifiers);

	while (gf_list_count(ptr->fragment_run_entry_table)) {
		gf_free(gf_list_get(ptr->fragment_run_entry_table, 0));
		gf_list_rem(ptr->fragment_run_entry_table, 0);
	}
	gf_list_del(ptr->fragment_run_entry_table);

	gf_free(ptr);
}

fixed #1753


int sqlite3Fts5UnicodeCategory(u32 iCode) {
  int iRes = -1;
  int iHi;
  int iLo;
  int ret;
  u16 iKey;

  if( iCode>=(1<<20) ){
    return 0;
  }
  iLo = aFts5UnicodeBlock[(iCode>>16)];
  iHi = aFts5UnicodeBlock[1+(iCode>>16)];
  iKey = (iCode & 0xFFFF);
  while( iHi>iLo ){
    int iTest = (iHi + iLo) / 2;
    assert( iTest>=iLo && iTest<iHi );
    if( iKey>=aFts5UnicodeMap[iTest] ){
      iRes = iTest;
      iLo = iTest+1;
    }else{
      iHi = iTest;
    }
  }

  if( iRes<0 ) return 0;
  if( iKey>=(aFts5UnicodeMap[iRes]+(aFts5UnicodeData[iRes]>>5)) ) return 0;
  ret = aFts5UnicodeData[iRes] & 0x1F;
  if( ret!=30 ) return ret;
  return ((iKey - aFts5UnicodeMap[iRes]) & 0x01) ? 5 : 9;
}

Prevent fts5 tokenizer unicode61 from considering '\0' to be a token characters, even if other characters of class "Cc" are.

FossilOrigin-Name: b7b7bde9b7a03665e3691c6d51118965f216d2dfb1617f138b9f9e60e418ed2f


int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){
  /* Each entry in the following array defines a rule for folding a range
  ** of codepoints to lower case. The rule applies to a range of nRange
  ** codepoints starting at codepoint iCode.
  **
  ** If the least significant bit in flags is clear, then the rule applies
  ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
  ** need to be folded). Or, if it is set, then the rule only applies to
  ** every second codepoint in the range, starting with codepoint C.
  **
  ** The 7 most significant bits in flags are an index into the aiOff[]
  ** array. If a specific codepoint C does require folding, then its lower
  ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
  **
  ** The contents of this array are generated by parsing the CaseFolding.txt
  ** file distributed as part of the "Unicode Character Database". See
  ** http://www.unicode.org for details.
  */
  static const struct TableEntry {
    unsigned short iCode;
    unsigned char flags;
    unsigned char nRange;
  } aEntry[] = {
    {65, 14, 26},          {181, 64, 1},          {192, 14, 23},
    {216, 14, 7},          {256, 1, 48},          {306, 1, 6},
    {313, 1, 16},          {330, 1, 46},          {376, 116, 1},
    {377, 1, 6},           {383, 104, 1},         {385, 50, 1},
    {386, 1, 4},           {390, 44, 1},          {391, 0, 1},
    {393, 42, 2},          {395, 0, 1},           {398, 32, 1},
    {399, 38, 1},          {400, 40, 1},          {401, 0, 1},
    {403, 42, 1},          {404, 46, 1},          {406, 52, 1},
    {407, 48, 1},          {408, 0, 1},           {412, 52, 1},
    {413, 54, 1},          {415, 56, 1},          {416, 1, 6},
    {422, 60, 1},          {423, 0, 1},           {425, 60, 1},
    {428, 0, 1},           {430, 60, 1},          {431, 0, 1},
    {433, 58, 2},          {435, 1, 4},           {439, 62, 1},
    {440, 0, 1},           {444, 0, 1},           {452, 2, 1},
    {453, 0, 1},           {455, 2, 1},           {456, 0, 1},
    {458, 2, 1},           {459, 1, 18},          {478, 1, 18},
    {497, 2, 1},           {498, 1, 4},           {502, 122, 1},
    {503, 134, 1},         {504, 1, 40},          {544, 110, 1},
    {546, 1, 18},          {570, 70, 1},          {571, 0, 1},
    {573, 108, 1},         {574, 68, 1},          {577, 0, 1},
    {579, 106, 1},         {580, 28, 1},          {581, 30, 1},
    {582, 1, 10},          {837, 36, 1},          {880, 1, 4},
    {886, 0, 1},           {902, 18, 1},          {904, 16, 3},
    {908, 26, 1},          {910, 24, 2},          {913, 14, 17},
    {931, 14, 9},          {962, 0, 1},           {975, 4, 1},
    {976, 140, 1},         {977, 142, 1},         {981, 146, 1},
    {982, 144, 1},         {984, 1, 24},          {1008, 136, 1},
    {1009, 138, 1},        {1012, 130, 1},        {1013, 128, 1},
    {1015, 0, 1},          {1017, 152, 1},        {1018, 0, 1},
    {1021, 110, 3},        {1024, 34, 16},        {1040, 14, 32},
    {1120, 1, 34},         {1162, 1, 54},         {1216, 6, 1},
    {1217, 1, 14},         {1232, 1, 88},         {1329, 22, 38},
    {4256, 66, 38},        {4295, 66, 1},         {4301, 66, 1},
    {7680, 1, 150},        {7835, 132, 1},        {7838, 96, 1},
    {7840, 1, 96},         {7944, 150, 8},        {7960, 150, 6},
    {7976, 150, 8},        {7992, 150, 8},        {8008, 150, 6},
    {8025, 151, 8},        {8040, 150, 8},        {8072, 150, 8},
    {8088, 150, 8},        {8104, 150, 8},        {8120, 150, 2},
    {8122, 126, 2},        {8124, 148, 1},        {8126, 100, 1},
    {8136, 124, 4},        {8140, 148, 1},        {8152, 150, 2},
    {8154, 120, 2},        {8168, 150, 2},        {8170, 118, 2},
    {8172, 152, 1},        {8184, 112, 2},        {8186, 114, 2},
    {8188, 148, 1},        {8486, 98, 1},         {8490, 92, 1},
    {8491, 94, 1},         {8498, 12, 1},         {8544, 8, 16},
    {8579, 0, 1},          {9398, 10, 26},        {11264, 22, 47},
    {11360, 0, 1},         {11362, 88, 1},        {11363, 102, 1},
    {11364, 90, 1},        {11367, 1, 6},         {11373, 84, 1},
    {11374, 86, 1},        {11375, 80, 1},        {11376, 82, 1},
    {11378, 0, 1},         {11381, 0, 1},         {11390, 78, 2},
    {11392, 1, 100},       {11499, 1, 4},         {11506, 0, 1},
    {42560, 1, 46},        {42624, 1, 24},        {42786, 1, 14},
    {42802, 1, 62},        {42873, 1, 4},         {42877, 76, 1},
    {42878, 1, 10},        {42891, 0, 1},         {42893, 74, 1},
    {42896, 1, 4},         {42912, 1, 10},        {42922, 72, 1},
    {65313, 14, 26},
  };
  static const unsigned short aiOff[] = {
   1,     2,     8,     15,    16,    26,    28,    32,
   37,    38,    40,    48,    63,    64,    69,    71,
   79,    80,    116,   202,   203,   205,   206,   207,
   209,   210,   211,   213,   214,   217,   218,   219,
   775,   7264,  10792, 10795, 23228, 23256, 30204, 54721,
   54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
   57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
   65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
   65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
   65514, 65521, 65527, 65528, 65529,
  };

  int ret = c;

  assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );

  if( c<128 ){
    if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
  }else if( c<65536 ){
    const struct TableEntry *p;
    int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
    int iLo = 0;
    int iRes = -1;

    assert( c>aEntry[0].iCode );
    while( iHi>=iLo ){
      int iTest = (iHi + iLo) / 2;
      int cmp = (c - aEntry[iTest].iCode);
      if( cmp>=0 ){
        iRes = iTest;
        iLo = iTest+1;
      }else{
        iHi = iTest-1;
      }
    }

    assert( iRes>=0 && c>=aEntry[iRes].iCode );
    p = &aEntry[iRes];
    if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
      ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
      assert( ret>0 );
    }

    if( eRemoveDiacritic ){
      ret = fts5_remove_diacritic(ret, eRemoveDiacritic==2);
    }
  }

  else if( c>=66560 && c<66600 ){
    ret = c + 40;
  }

  return ret;
}


void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
  int i = 0;
  int iTbl = 0;
  while( i<128 ){
    int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ];
    int n = (aFts5UnicodeData[iTbl] >> 5) + i;
    for(; i<128 && i<n; i++){
      aAscii[i] = (u8)bToken;
    }
    iTbl++;
  }
  aAscii[0] = 0;                  /* 0x00 is never a token character */
}


int sqlite3Fts5UnicodeIsdiacritic(int c){
  unsigned int mask0 = 0x08029FDF;
  unsigned int mask1 = 0x000361F8;
  if( c<768 || c>817 ) return 0;
  return (c < 768+32) ?
      (mask0 & ((unsigned int)1 << (c-768))) :
      (mask1 & ((unsigned int)1 << (c-768-32)));
}


int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){
  aArray[0] = 1;
  switch( zCat[0] ){
    case 'C':
          switch( zCat[1] ){
            case 'c': aArray[1] = 1; break;
            case 'f': aArray[2] = 1; break;
            case 'n': aArray[3] = 1; break;
            case 's': aArray[4] = 1; break;
            case 'o': aArray[31] = 1; break;
            case '*':
              aArray[1] = 1;
              aArray[2] = 1;
              aArray[3] = 1;
              aArray[4] = 1;
              aArray[31] = 1;
              break;
            default: return 1;          }
          break;

    case 'L':
          switch( zCat[1] ){
            case 'l': aArray[5] = 1; break;
            case 'm': aArray[6] = 1; break;
            case 'o': aArray[7] = 1; break;
            case 't': aArray[8] = 1; break;
            case 'u': aArray[9] = 1; break;
            case 'C': aArray[30] = 1; break;
            case '*':
              aArray[5] = 1;
              aArray[6] = 1;
              aArray[7] = 1;
              aArray[8] = 1;
              aArray[9] = 1;
              aArray[30] = 1;
              break;
            default: return 1;          }
          break;

    case 'M':
          switch( zCat[1] ){
            case 'c': aArray[10] = 1; break;
            case 'e': aArray[11] = 1; break;
            case 'n': aArray[12] = 1; break;
            case '*':
              aArray[10] = 1;
              aArray[11] = 1;
              aArray[12] = 1;
              break;
            default: return 1;          }
          break;

    case 'N':
          switch( zCat[1] ){
            case 'd': aArray[13] = 1; break;
            case 'l': aArray[14] = 1; break;
            case 'o': aArray[15] = 1; break;
            case '*':
              aArray[13] = 1;
              aArray[14] = 1;
              aArray[15] = 1;
              break;
            default: return 1;          }
          break;

    case 'P':
          switch( zCat[1] ){
            case 'c': aArray[16] = 1; break;
            case 'd': aArray[17] = 1; break;
            case 'e': aArray[18] = 1; break;
            case 'f': aArray[19] = 1; break;
            case 'i': aArray[20] = 1; break;
            case 'o': aArray[21] = 1; break;
            case 's': aArray[22] = 1; break;
            case '*':
              aArray[16] = 1;
              aArray[17] = 1;
              aArray[18] = 1;
              aArray[19] = 1;
              aArray[20] = 1;
              aArray[21] = 1;
              aArray[22] = 1;
              break;
            default: return 1;          }
          break;

    case 'S':
          switch( zCat[1] ){
            case 'c': aArray[23] = 1; break;
            case 'k': aArray[24] = 1; break;
            case 'm': aArray[25] = 1; break;
            case 'o': aArray[26] = 1; break;
            case '*':
              aArray[23] = 1;
              aArray[24] = 1;
              aArray[25] = 1;
              aArray[26] = 1;
              break;
            default: return 1;          }
          break;

    case 'Z':
          switch( zCat[1] ){
            case 'l': aArray[27] = 1; break;
            case 'p': aArray[28] = 1; break;
            case 's': aArray[29] = 1; break;
            case '*':
              aArray[27] = 1;
              aArray[28] = 1;
              aArray[29] = 1;
              break;
            default: return 1;          }
          break;

  }
  return 0;
}


static NTSTATUS kdc_check_generic_kerberos(struct irpc_message *msg,
				 struct kdc_check_generic_kerberos *r)
{
	struct PAC_Validate pac_validate;
	DATA_BLOB srv_sig;
	struct PAC_SIGNATURE_DATA kdc_sig;
	struct kdc_server *kdc = talloc_get_type(msg->private_data, struct kdc_server);
	krb5_kdc_configuration *kdc_config =
		(krb5_kdc_configuration *)kdc->private_data;
	enum ndr_err_code ndr_err;
	int ret;
	hdb_entry ent;
	krb5_principal principal;

	/* There is no reply to this request */
	r->out.generic_reply = data_blob(NULL, 0);

	ndr_err = ndr_pull_struct_blob(&r->in.generic_request, msg, &pac_validate,
				       (ndr_pull_flags_fn_t)ndr_pull_PAC_Validate);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (pac_validate.MessageType != NETLOGON_GENERIC_KRB5_PAC_VALIDATE) {
		/* We don't implement any other message types - such as certificate validation - yet */
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (pac_validate.ChecksumAndSignature.length != (pac_validate.ChecksumLength + pac_validate.SignatureLength)
	    || pac_validate.ChecksumAndSignature.length < pac_validate.ChecksumLength
	    || pac_validate.ChecksumAndSignature.length < pac_validate.SignatureLength ) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	srv_sig = data_blob_const(pac_validate.ChecksumAndSignature.data,
				  pac_validate.ChecksumLength);

	ret = krb5_make_principal(kdc->smb_krb5_context->krb5_context, &principal,
				  lpcfg_realm(kdc->task->lp_ctx),
				  "krbtgt", lpcfg_realm(kdc->task->lp_ctx),
				  NULL);

	if (ret != 0) {
		return NT_STATUS_NO_MEMORY;
	}

	ret = kdc_config->db[0]->hdb_fetch_kvno(kdc->smb_krb5_context->krb5_context,
						 kdc_config->db[0],
						 principal,
						 HDB_F_GET_KRBTGT | HDB_F_DECRYPT,
						 0,
						 &ent);

	if (ret != 0) {
		hdb_free_entry(kdc->smb_krb5_context->krb5_context, kdc_config->db[0], &ent);
		krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);

		return NT_STATUS_LOGON_FAILURE;
	}

	kdc_sig.type = pac_validate.SignatureType;
	kdc_sig.signature = data_blob_const(&pac_validate.ChecksumAndSignature.data[pac_validate.ChecksumLength],
					    pac_validate.SignatureLength);

	ret = kdc_check_pac(kdc->smb_krb5_context->krb5_context, srv_sig, &kdc_sig, &ent);

	hdb_free_entry(kdc->smb_krb5_context->krb5_context, kdc_config->db[0], &ent);
	krb5_free_principal(kdc->smb_krb5_context->krb5_context, principal);

	if (ret != 0) {
		return NT_STATUS_LOGON_FAILURE;
	}

	return NT_STATUS_OK;
}

CVE-2022-32744 s4:kdc: Don't allow HDB keytab iteration

A fallback in krb5_rd_req_ctx() means that Samba's kpasswd service will
try many inappropriate keys to decrypt the ticket supplied to it. For
example, it will accept a ticket encrypted with the Administrator's key,
when it should rather accept only tickets encrypted with the krbtgt's
key (and not an RODC krbtgt). To fix this, declare the HDB keytab using
the HDBGET ops, which do not support iteration.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
Reviewed-by: Andreas Schneider <asn@samba.org>


static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc,
				       struct loadparm_context *lp_ctx,
				       struct interface *ifaces,
				       const struct model_ops *model_ops)
{
	int num_interfaces;
	TALLOC_CTX *tmp_ctx = talloc_new(kdc);
	NTSTATUS status;
	int i;
	uint16_t kdc_port = lpcfg_krb5_port(lp_ctx);
	uint16_t kpasswd_port = lpcfg_kpasswd_port(lp_ctx);
	bool done_wildcard = false;

	num_interfaces = iface_list_count(ifaces);

	/* if we are allowing incoming packets from any address, then
	   we need to bind to the wildcard address */
	if (!lpcfg_bind_interfaces_only(lp_ctx)) {
		size_t num_binds = 0;
		char **wcard = iface_list_wildcard(kdc);
		NT_STATUS_HAVE_NO_MEMORY(wcard);
		for (i=0; wcard[i]; i++) {
			if (kdc_port) {
				status = kdc_add_socket(kdc, model_ops,
							"kdc", wcard[i], kdc_port,
							kdc_process, false);
				if (NT_STATUS_IS_OK(status)) {
					num_binds++;
				}
			}

			if (kpasswd_port) {
				status = kdc_add_socket(kdc, model_ops,
							"kpasswd", wcard[i], kpasswd_port,
							kpasswd_process, false);
				if (NT_STATUS_IS_OK(status)) {
					num_binds++;
				}
			}
		}
		talloc_free(wcard);
		if (num_binds == 0) {
			return NT_STATUS_INVALID_PARAMETER_MIX;
		}
		done_wildcard = true;
	}

	for (i=0; i<num_interfaces; i++) {
		const char *address = talloc_strdup(tmp_ctx, iface_list_n_ip(ifaces, i));

		if (kdc_port) {
			status = kdc_add_socket(kdc, model_ops,
						"kdc", address, kdc_port,
						kdc_process, done_wildcard);
			NT_STATUS_NOT_OK_RETURN(status);
		}

		if (kpasswd_port) {
			status = kdc_add_socket(kdc, model_ops,
						"kpasswd", address, kpasswd_port,
						kpasswd_process, done_wildcard);
			NT_STATUS_NOT_OK_RETURN(status);
		}
	}

	talloc_free(tmp_ctx);

	return NT_STATUS_OK;
}


NTSTATUS server_service_kdc_init(TALLOC_CTX *ctx)
{
	static const struct service_details details = {
		.inhibit_fork_on_accept = true,
		.inhibit_pre_fork = false,
		.task_init = kdc_task_init,
		.post_fork = kdc_post_fork
	};
	return register_server_service(ctx, "kdc", &details);
}


static kdc_code kdc_process(struct kdc_server *kdc,
			    TALLOC_CTX *mem_ctx,
			    DATA_BLOB *input,
			    DATA_BLOB *reply,
			    struct tsocket_address *peer_addr,
			    struct tsocket_address *my_addr,
			    int datagram_reply)
{
	int ret;
	char *pa;
	struct sockaddr_storage ss;
	krb5_data k5_reply;
	krb5_kdc_configuration *kdc_config =
		(krb5_kdc_configuration *)kdc->private_data;

	krb5_data_zero(&k5_reply);

	krb5_kdc_update_time(NULL);

	ret = tsocket_address_bsd_sockaddr(peer_addr, (struct sockaddr *) &ss,
				sizeof(struct sockaddr_storage));
	if (ret < 0) {
		return KDC_ERROR;
	}
	pa = tsocket_address_string(peer_addr, mem_ctx);
	if (pa == NULL) {
		return KDC_ERROR;
	}

	DBG_DEBUG("Received KDC packet of length %lu from %s\n",
		  (long)input->length - 4, pa);

	ret = krb5_kdc_process_krb5_request(kdc->smb_krb5_context->krb5_context,
					    kdc_config,
					    input->data, input->length,
					    &k5_reply,
					    pa,
					    (struct sockaddr *) &ss,
					    datagram_reply);
	if (ret == -1) {
		*reply = data_blob(NULL, 0);
		return KDC_ERROR;
	}

	if (ret == HDB_ERR_NOT_FOUND_HERE) {
		*reply = data_blob(NULL, 0);
		return KDC_PROXY_REQUEST;
	}

	if (k5_reply.length) {
		*reply = data_blob_talloc(mem_ctx, k5_reply.data, k5_reply.length);
		krb5_data_free(&k5_reply);
	} else {
		*reply = data_blob(NULL, 0);
	}
	return KDC_OK;
}


static NTSTATUS kdc_task_init(struct task_server *task)
{
	struct kdc_server *kdc;
	NTSTATUS status;
	struct interface *ifaces;

	switch (lpcfg_server_role(task->lp_ctx)) {
	case ROLE_STANDALONE:
		task_server_terminate(task, "kdc: no KDC required in standalone configuration", false);
		return NT_STATUS_INVALID_DOMAIN_ROLE;
	case ROLE_DOMAIN_MEMBER:
		task_server_terminate(task, "kdc: no KDC required in member server configuration", false);
		return NT_STATUS_INVALID_DOMAIN_ROLE;
	case ROLE_DOMAIN_PDC:
	case ROLE_DOMAIN_BDC:
	case ROLE_IPA_DC:
		task_server_terminate(
		    task, "Cannot start KDC as a 'classic Samba' DC", false);
		return NT_STATUS_INVALID_DOMAIN_ROLE;
	case ROLE_ACTIVE_DIRECTORY_DC:
		/* Yes, we want a KDC */
		break;
	}

	load_interface_list(task, task->lp_ctx, &ifaces);

	if (iface_list_count(ifaces) == 0) {
		task_server_terminate(task, "kdc: no network interfaces configured", false);
		return NT_STATUS_UNSUCCESSFUL;
	}

	task_server_set_title(task, "task[kdc]");

	kdc = talloc_zero(task, struct kdc_server);
	if (kdc == NULL) {
		task_server_terminate(task, "kdc: out of memory", true);
		return NT_STATUS_NO_MEMORY;
	}

	kdc->task = task;
	task->private_data = kdc;

	/* start listening on the configured network interfaces */
	status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces,
					task->model_ops);
	if (!NT_STATUS_IS_OK(status)) {
		task_server_terminate(task, "kdc failed to setup interfaces", true);
		return status;
	}

	return NT_STATUS_OK;
}


void enc_untrusted_enable_waiting(int32_t *const queue) {
  enc_untrusted_wait_queue_set_value(queue, kWaitQueueEnabled);
}

Check untrusted queue is in outside enclave

PiperOrigin-RevId: 333370935
Change-Id: Ic3f15d5db1302d95c7cb199b44172474fecb81ca

static constexpr int32_t kWaitQueueEnabled = 0;

static constexpr int32_t kWaitQueueDisabled = 1;


int32_t *enc_untrusted_create_wait_queue() {
  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(sizeof(int32_t));
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kLocalLifetimeAllocHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_create_wait_queue",
                           2);
  int32_t *queue = reinterpret_cast<int32_t *>(output.next<uintptr_t>());
  if (!TrustedPrimitives::IsOutsideEnclave(queue, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_create_wait_queue: queue should be in untrusted memory");
  }
  int klinux_errno = output.next<int>();
  if (queue == nullptr) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  enc_untrusted_disable_waiting(queue);
  return queue;
}


void enc_untrusted_disable_waiting(int32_t *const queue) {
  enc_untrusted_wait_queue_set_value(queue, kWaitQueueDisabled);
}


int enc_untrusted_sys_futex_wake(int32_t *futex, int32_t num) {
  if (!TrustedPrimitives::IsOutsideEnclave(futex, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_sys_futex_wake: futex word should be in untrusted "
        "local memory.");
  }

  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(reinterpret_cast<uint64_t>(futex));
  input.Push<int32_t>(num);
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kSysFutexWakeHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_sys_futex_wake", 2);
  int result = output.next<int>();
  int klinux_errno = output.next<int>();
  if (result == -1) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  return result;
}


void enc_untrusted_thread_wait_value(int32_t *const queue, int32_t value,
                                     uint64_t timeout_microsec) {
  enc_untrusted_sys_futex_wait(queue, value, timeout_microsec);
}


void enc_untrusted_notify(int32_t *const queue, int32_t num_threads) {
  enc_untrusted_sys_futex_wake(queue, num_threads);
}


void enc_untrusted_thread_wait(int32_t *const queue,
                               uint64_t timeout_microsec) {
  enc_untrusted_thread_wait_value(queue, kWaitQueueEnabled, timeout_microsec);
}


void enc_untrusted_destroy_wait_queue(int32_t *const queue) {
  // This is a no op on purpose. Wait queue pointers are now
  // registered to be freed on enclave exit.
}


int enc_untrusted_sys_futex_wait(int32_t *futex, int32_t expected,
                                 int64_t timeout_microsec) {
  if (!TrustedPrimitives::IsOutsideEnclave(futex, sizeof(int32_t))) {
    TrustedPrimitives::BestEffortAbort(
        "enc_untrusted_sys_futex_wait: futex word should be in untrusted "
        "local memory.");
  }

  MessageWriter input;
  MessageReader output;
  input.Push<uint64_t>(reinterpret_cast<uint64_t>(futex));
  input.Push<int32_t>(expected);
  input.Push<int64_t>(timeout_microsec);
  const auto status = NonSystemCallDispatcher(
      ::asylo::host_call::kSysFutexWaitHandler, &input, &output);
  CheckStatusAndParamCount(status, output, "enc_untrusted_sys_futex_wait", 2);
  int result = output.next<int>();
  int klinux_errno = output.next<int>();

  // If FUTEX_WAIT successfully causes the thread to be suspended in the kernel,
  // it returns a zero when the caller is woken up. Otherwise, it returns the
  // appropriate errno.
  if (result != 0) {
    errno = FromkLinuxErrorNumber(klinux_errno);
  }
  return result;
}


void enc_untrusted_wait_queue_set_value(int32_t *const queue, int32_t value) {
  TrustedPrimitives::UntrustedLocalMemcpy(queue, &value, sizeof(int32_t));
}


void CClient::Disconnected() {
    DEBUG(GetSockName() << " == Disconnected()");
    CIRCNetwork* pNetwork = m_pNetwork;
    SetNetwork(nullptr, false, false);

    if (m_pUser) {
        NETWORKMODULECALL(OnClientDisconnect(), m_pUser, pNetwork, this,
                          NOTHING);
    }
}

Fix echo-message for *status

Close #1705

#define CALLMOD(MOD, CLIENT, USER, NETWORK, FUNC)                             \
    {                                                                         \
        CModule* pModule = nullptr;                                           \
        if (NETWORK && (pModule = (NETWORK)->GetModules().FindModule(MOD))) { \
            try {                                                             \
                CClient* pOldClient = pModule->GetClient();                   \
                pModule->SetClient(CLIENT);                                   \
                pModule->FUNC;                                                \
                pModule->SetClient(pOldClient);                               \
            } catch (const CModule::EModException& e) {                       \
                if (e == CModule::UNLOAD) {                                   \
                    (NETWORK)->GetModules().UnloadModule(MOD);                \
                }                                                             \
            }                                                                 \
        } else if ((pModule = (USER)->GetModules().FindModule(MOD))) {        \
            try {                                                             \
                CClient* pOldClient = pModule->GetClient();                   \
                CIRCNetwork* pOldNetwork = pModule->GetNetwork();             \
                pModule->SetClient(CLIENT);                                   \
                pModule->SetNetwork(NETWORK);                                 \
                pModule->FUNC;                                                \
                pModule->SetClient(pOldClient);                               \
                pModule->SetNetwork(pOldNetwork);                             \
            } catch (const CModule::EModException& e) {                       \
                if (e == CModule::UNLOAD) {                                   \
                    (USER)->GetModules().UnloadModule(MOD);                   \
                }                                                             \
            }                                                                 \
        } else if ((pModule = CZNC::Get().GetModules().FindModule(MOD))) {    \
            try {                                                             \
                CClient* pOldClient = pModule->GetClient();                   \
                CIRCNetwork* pOldNetwork = pModule->GetNetwork();             \
                CUser* pOldUser = pModule->GetUser();                         \
                pModule->SetClient(CLIENT);                                   \
                pModule->SetNetwork(NETWORK);                                 \
                pModule->SetUser(USER);                                       \
                pModule->FUNC;                                                \
                pModule->SetClient(pOldClient);                               \
                pModule->SetNetwork(pOldNetwork);                             \
                pModule->SetUser(pOldUser);                                   \
            } catch (const CModule::EModException& e) {                       \
                if (e == CModule::UNLOAD) {                                   \
                    CZNC::Get().GetModules().UnloadModule(MOD);               \
                }                                                             \
            }                                                                 \
        } else {                                                              \
            PutStatus(t_f("No such module {1}")(MOD));                        \
        }                                                                     \
    }


void CClient::StatusCTCP(const CString& sLine) {
    CString sCommand = sLine.Token(0);

    if (sCommand.Equals("PING")) {
        PutStatusNotice("\001PING " + sLine.Token(1, true) + "\001");
    } else if (sCommand.Equals("VERSION")) {
        PutStatusNotice("\001VERSION " + CZNC::GetTag() + "\001");
    }
}


TEST_F(ZNCTest, Encoding) {
    auto znc = Run();
    auto ircd = ConnectIRCd();
    auto client = LoginClient();
    ircd.Write(":server 001 nick :hello");
    // legacy
    ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
    client.ReadUntil("Hello\xE6world");
    client.Write("PRIVMSG *controlpanel :SetNetwork Encoding $me $net UTF-8");
    client.ReadUntil("Encoding = UTF-8");
    ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
    client.ReadUntil("Hello\xEF\xBF\xBDworld");
    client.Write(
        "PRIVMSG *controlpanel :SetNetwork Encoding $me $net ^CP-1251");
    client.ReadUntil("Encoding = ^CP-1251");
    ircd.Write(":n!u@h PRIVMSG nick :Hello\xE6world");
    client.ReadUntil("Hello\xD0\xB6world");
    ircd.Write(":n!u@h PRIVMSG nick :Hello\xD0\xB6world");
    client.ReadUntil("Hello\xD0\xB6world");
}