#include #include #include #include #include "squirrel.h" #include "sqstdblobimpl.h" SQ_OPT_STRING_STRLEN(); #ifdef USE_SQ_SQLITE3 #include "lsqlite3.h" #endif #ifdef USE_AXTLS #ifdef __cplusplus extern "C" { #endif void * SSL_CTX_new(void *); #ifdef __cplusplus } #endif #endif #define USE_MG_MD5 1 #include "mongoose.h" #define PRINT_FILE_LINE printf("%s %d\n", __FILE__,__LINE__); #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ x #else # define UNUSED(x) x #endif #define MARK() do { \ printf("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__); \ } while (0) typedef struct SQ_Mg_Context SQ_Mg_Context; typedef struct SQ_MG_Callback SQ_MG_Callback; struct SQ_MG_Callback { size_t size; char buf[1]; }; static SQ_MG_Callback *SQ_MG_Callback_malloc(size_t x){ SQ_MG_Callback *p = (SQ_MG_Callback *)sq_malloc(sizeof(SQ_MG_Callback)+x); if(p) p->size = x; return p; } static void SQ_MG_Callback_free(SQ_MG_Callback *p){ if(p) sq_free(p, sizeof(SQ_MG_Callback)+p->size); } struct SQ_Mg_Context { HSQUIRRELVM v; struct mg_context *ctx; SQ_MG_Callback *master_plugin; SQ_MG_Callback *master_plugin_exit; SQ_MG_Callback *user_callback; SQ_MG_Callback *user_callback_setup; SQ_MG_Callback *user_callback_exit; }; static const SQChar sq_mg_context_TAG[] = "sq_mg_conn_class"; #define GET_mg_context_INSTANCE() \ SQ_Mg_Context *self; \ if((_rc_ = sq_getinstanceup(v,1,(SQUserPointer*)&self,(void*)sq_mg_context_TAG)) < 0) return _rc_; static const SQChar sq_mg_user_callback[] = "mg_user_callback"; static const SQChar sq_http_request_TAG[] = "HttpRequest"; #define GET_http_request_INSTANCE() \ struct mg_connection *conn; \ if((_rc_ = sq_getinstanceup(v,1,(SQUserPointer*)&conn,(void*)sq_http_request_TAG)) < 0) return _rc_; static SQRESULT sq_http_request_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) { return 1; } static SQRESULT sq_http_request_constructor(HSQUIRRELVM v) { //SQ_FUNC_VARS_NO_TOP(v); struct mg_connection *conn = 0; sq_setinstanceup(v, 1, conn); sq_setreleasehook(v,1, sq_http_request_releasehook); return 1; } static SQRESULT sq_http_request_print(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_http_request_INSTANCE(); SQInteger i, write_count = 0; for (i = 2; i <= _top_; ++i) { sq_tostring(v, i); SQ_GET_STRING(v, -1, value); write_count += mg_write(conn, value, value_size); sq_poptop(v); } sq_pushinteger(v, write_count); return 1; } #ifdef USE_SQ_LSQLITE3 static SQRESULT sq_http_request_vm_print(HSQUIRRELVM v) { GET_http_request_INSTANCE(); lsqlite3_sdb_vm *svm = (lsqlite3_sdb_vm *)luaL_checkudata(v, 1, lsqlite3_sqlite_vm_meta); SQInteger idx = luaL_checkint(v,2); SQInteger len = sqlite3_column_bytes(svm->vm, idx); if(len) sq_pushinteger(v, mg_write(conn, sqlite3_column_text(svm->vm, idx), len)); else sq_pushinteger(v, 0); return 1; } #endif static SQRESULT sq_http_request_write(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, buf); SQ_OPT_INTEGER(v, 3, write_size, buf_size); sq_pushinteger(v, mg_write(conn, buf, write_size)); return 1; } static SQRESULT sq_http_request_read(HSQUIRRELVM v) { SQ_FUNC_VARS(v); GET_http_request_INSTANCE(); SQ_OPT_INTEGER(v, 2, n, 1024*2000); size_t rlen; /* how much to read */ size_t nr; /* number of chars actually read */ rlen = 8192; /* try to read that much each time */ SQBlob blob(0, rlen); if (rlen > n) rlen = n; /* cannot read more than asked */ char *p = sq_getscratchpad(v,rlen); do { nr = mg_read(conn, p, rlen); blob.Write(p, nr); n -= nr; /* still have to read `n' chars */ } while (n > 0 && nr == rlen); /* until end of count or eof */ sq_pushstring(v, (const SQChar *)blob.GetBuf(), blob.Len()); /* close buffer */ return 1; } static SQRESULT sq_http_request_send_file(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, file_path); mg_send_file(conn, file_path); return 0; } static SQRESULT sq_http_request_write_blob(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQBlob *blob = NULL; { if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&blob,(SQUserPointer)SQBlob::SQBlob_TAG))) return sq_throwerror(v,_SC("invalid type tag")); } if(!blob || !blob->IsValid()) return sq_throwerror(v,_SC("the blob is invalid")); sq_pushinteger(v, mg_write(conn, (const SQChar*)blob->GetBuf(), blob->Len())); return 1; } static SQRESULT sq_http_request_get_header(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, name); sq_pushstring(v, mg_get_header(conn, name), -1); return 1; } static SQRESULT sq_http_request_get_cookie(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, cookie_name); const char *start; int len = mg_find_cookie(conn, cookie_name, &start); if(len > 0) sq_pushstring(v, start, len); else sq_pushnull(v); return 1; } static SQRESULT sq_http_request_get_var(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, data); SQ_GET_STRING(v, 3, name); const char *start; size_t buffer_len; int var_len = mg_find_var(data, data_size, name, &start); if(var_len > 0){ buffer_len = var_len+1; char *buffer = sq_getscratchpad(v,buffer_len); if(buffer){ var_len = mg_url_decode(start, var_len, buffer, buffer_len, 1); sq_pushstring(v, buffer, var_len); return 1; } } sq_pushnull(v); return 1; } static SQRESULT sq_http_request_handle_cgi_request(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, prog); mg_handle_cgi_request(conn, prog); return 0; } static SQRESULT sq_http_request_get_option(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, name); SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) mg_get_user_data(conn); sq_pushstring(v, mg_get_option(sq_mg_ctx->ctx, name), -1); return 1; } //Digest authentication functions #define MAX_USER_LEN 20 #define MAX_NONCE_LEN 36 //extra bytes for zero terminator #define MAX_SESSIONS 5 #define SESSION_TTL 3600 #define SESSION_REQUEST_TTL 120 enum e_session_state {e_session_invalid, e_session_sent_request, e_session_authorized}; struct st_session_t { int state; char nonce[MAX_NONCE_LEN]; char user[MAX_USER_LEN]; // Authenticated user long last_nc; time_t last_access; // last_access time long remote_ip; // Client's IP address }; static struct st_session_t sessions[MAX_SESSIONS]; // Current sessions // Protects messages, sessions, last_message_id static mg_thread_mutex_t session_rwlock; //should be called only after locking session_rwlock static struct st_session_t *my_get_session_only_after_lock(const char *nonce){ int i; for(i=0; i < MAX_SESSIONS; ++i){ //printf("%s : %s\n", sessions[i].nonce, ah->nonce); if(strcmp(sessions[i].nonce, nonce) == 0){ return &sessions[i]; } } return 0; } // Check the user's password, return 1 if OK static SQBool my_check_password(const struct mg_request_info *request_info, const char *my_tmp_password) { md5_buf_t ha2, expected_response; struct mg_auth_header *ah = request_info->ah; // Some of the parameters may be NULL if (request_info->request_method == NULL || //strcmp(ri->request_method, "PUT") == 0 || //strcmp(ri->request_method, "DELETE") == 0 || ah == NULL || ah->nonce == NULL || ah->nc == NULL || ah->cnonce == NULL || ah->qop == NULL || ah->uri == NULL || ah->response == NULL) { return SQFalse; } // NOTE(lsm): due to a bug in MSIE, we do not compare the URI // TODO(lsm): check for authentication timeout if (// strcmp(ah->uri, conn->request_info.uri) != 0 || strlen(ah->response) != 32 ) { return SQFalse; } mg_md5(ha2, request_info->request_method, ":", ah->uri, NULL); mg_md5(expected_response, my_tmp_password /*ah->ha1*/, ":", ah->nonce, ":", ah->nc, ":", ah->cnonce, ":", ah->qop, ":", ha2, NULL); SQBool result = mg_strcasecmp(ah->response, expected_response) == 0; //printf("%s : %s : %s\n", my_tmp_password, ah->response, expected_response); if(result) //lets check timeout and other issues { struct st_session_t *session_found = 0; result = SQFalse; mg_thread_mutex_lock(&session_rwlock); session_found = my_get_session_only_after_lock(ah->nonce); if(session_found) { do { //mobile ip is a pain for security if( /*(ses.remote_ip != request_info->remote_ip) ||*/ ((time(NULL) - session_found->last_access) > SESSION_TTL) ) break; if(session_found->state == e_session_sent_request) { session_found->state = e_session_authorized; snprintf(session_found->user, sizeof(session_found->user), "%s", ah->user); } else if(strcmp(session_found->user, ah->user) != 0) break; long recived_nc = strtol(ah->nc, 0, 16); time_t received_time = time(NULL); /* //printf("%d : %lu : %lu : %lu : %lu\n", request_info->remote_port, ses.last_nc, recived_nc, ses.last_access, received_time); if((ses.last_nc+1) != recived_nc){ //lets see if we can manage out of order sent by some browsers if((received_time - ses.last_access) > 2) break; //inside a window of 2 seconds we tolerate nc out of order if(ses.last_nc > recived_nc) recived_nc = ses.last_nc; } */ session_found->last_access = received_time; session_found->last_nc = recived_nc; result = SQTrue; } while(0); if(!result){ session_found->state = e_session_invalid; session_found->nonce[0] = '\0'; } } else { //dbg_msg("Session not found ! : %s : %s\n", ah->nonce, request_info->uri); } mg_thread_mutex_unlock(&session_rwlock); } return result; } static SQRESULT sq_http_request_check_password(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, my_tmp_password); const struct mg_request_info *request_info = mg_get_request_info(conn); sq_pushbool(v, my_check_password(request_info, my_tmp_password)); return 1; } // Close user session return 1 if closed static SQBool my_close_session(const struct mg_request_info *request_info) { SQBool result = SQFalse; struct st_session_t *session_found = 0; struct mg_auth_header *ah = request_info->ah; // Some of the parameters may be NULL if (request_info->request_method == NULL || //strcmp(ri->request_method, "PUT") == 0 || //strcmp(ri->request_method, "DELETE") == 0 || ah == NULL || ah->nonce == NULL || ah->nc == NULL || ah->cnonce == NULL || ah->qop == NULL || ah->uri == NULL || ah->response == NULL) { return 0; } mg_thread_mutex_lock(&session_rwlock); session_found = my_get_session_only_after_lock(ah->nonce); if(session_found) { session_found->state = e_session_invalid; session_found->nonce[0] = '\0'; result = SQTrue; } mg_thread_mutex_unlock(&session_rwlock); return result; } static SQRESULT sq_http_request_close_session(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); const struct mg_request_info *request_info = mg_get_request_info(conn); sq_pushbool(v, my_close_session(request_info)); return 1; } typedef char buf_1024_t[1024]; static const char *my_encode_nonce(buf_1024_t buf, md5_buf_t md5_buf, unsigned long ip) { snprintf(buf, sizeof(buf), "%lu:%d:%d:%lu", (unsigned long) time(NULL), rand(), rand(), ip); mg_md5(md5_buf, buf, NULL); return md5_buf; } static void my_send_authorization_request(struct mg_connection *conn, const struct mg_request_info *request_info, const char *authentication_domain, const char *nonce) { md5_buf_t md5_buf; buf_1024_t buf; int i; if (nonce == NULL) { nonce = my_encode_nonce(buf, md5_buf, (unsigned long) request_info->remote_ip); } struct st_session_t *available_session = NULL; mg_thread_mutex_lock(&session_rwlock); for(i=0; i<3; ++i) { for(i=0; i < MAX_SESSIONS; ++i){ if(sessions[i].state == e_session_invalid){ available_session = &sessions[i]; } if(sessions[i].state == e_session_sent_request){ if((time(NULL) - available_session->last_access) > SESSION_REQUEST_TTL){ //if session request bigger than 2 minutes reuse it available_session = &sessions[i]; } } } if(available_session){ available_session->state = e_session_sent_request; available_session->last_access = time(NULL); available_session->last_nc = 0; available_session->remote_ip = request_info->remote_ip; snprintf(available_session->nonce, sizeof(available_session->nonce), "%s", nonce); break; } else { nonce = my_encode_nonce(buf, md5_buf, (unsigned long) request_info->remote_ip); } } mg_thread_mutex_unlock(&session_rwlock); if(available_session) { i = snprintf(buf, sizeof(buf), "HTTP/1.1 401 Unauthorized\r\n" "Content-Length: 0\r\n" "WWW-Authenticate: Digest qop=\"auth\", " "realm=\"%s\", nonce=\"%s\"\r\n\r\n", authentication_domain, nonce); } else { i = snprintf(buf, sizeof(buf), "HTTP/1.1 503 Service Temporary Unavailable\r\n" "Content-Length: 0\r\n\r\n"); } mg_write(conn, buf, i); } static SQRESULT sq_http_request_send_authorization_request(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_http_request_INSTANCE(); SQ_GET_STRING(v, 2, authentication_domain); const struct mg_request_info *request_info = mg_get_request_info(conn); my_send_authorization_request(conn, request_info, authentication_domain, NULL); return 0; } #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_http_request_##name,nparams,tycheck} static SQRegFunction mg_http_request_methods[] = { _DECL_FUNC(constructor, 1, _SC("x")), _DECL_FUNC(print, -2, _SC("x n|s")), _DECL_FUNC(read, 2, _SC("xi")), _DECL_FUNC(write, 3, _SC("xsi")), _DECL_FUNC(write_blob, -2, _SC("xxi")), _DECL_FUNC(get_var, 3, _SC("xs")), _DECL_FUNC(get_cookie, 2, _SC("xs")), _DECL_FUNC(get_header, 2, _SC("xs")), _DECL_FUNC(send_file, 2, _SC("xs")), _DECL_FUNC(handle_cgi_request, 2, _SC("xs")), _DECL_FUNC(get_option, 2, _SC("xs")), _DECL_FUNC(check_password, 2, _SC("xs")), _DECL_FUNC(close_session, 1, _SC("x")), _DECL_FUNC(send_authorization_request, 2, _SC("xs")), {0,0} }; #undef _DECL_FUNC /***********/ // helper function to extract a single mg_config value from a Lua table static void fetchfield(HSQUIRRELVM v, int idx, const char *key, char **value, const char *d) { const char *s; sq_pushstring(v, key, -1); if(sq_rawget(v, idx) == SQ_OK){ if(sq_gettype(v, -1) != OT_NULL){ sq_tostring(v, -1); sq_getstring(v, -1, &s); *value = mg_strdup(s); sq_pop(v, 2); //retrieved value and converted value return; } } *value = NULL; } // initializes an options string array from a Lua table static SQRESULT fetchoptions(HSQUIRRELVM v, int idx, const char **options) { struct { const char *key; const char *value; } OPTIONS[] = { { "cgi_extensions", ".cgi,.pl,.php" }, { "cgi_environment", NULL }, { "put_delete_passwords_file", NULL }, { "cgi_interpreter", NULL }, { "protect_uri", NULL }, { "authentication_domain", "mydomain.com" }, { "ssi_extensions", ".shtml,.shtm" }, { "access_log_file", NULL }, { "ssl_chain_file", NULL }, { "enable_directory_listing", "yes" }, { "error_log_file", NULL }, { "global_passwords_file", NULL }, { "index_files", "index.html,index.htm,index.cgi" }, { "enable_keep_alive", "no" }, { "access_control_list", NULL }, { "max_request_size", "16384" }, { "extra_mime_types", NULL }, { "listening_ports", "8080" }, { "document_root", "." }, { "ssl_certificate", NULL }, { "num_threads", "10" }, { "run_as_user", NULL }, { NULL, NULL } }; char *value; int i, j; if(sq_gettype(v, idx) != OT_TABLE) return sq_throwerror(v, "a table is needed to retrieve options"); for (i = 0, j = 0; OPTIONS[i].key; ++i) { fetchfield(v, idx, OPTIONS[i].key, &value, OPTIONS[i].value); if (NULL != value) { options[j++] = mg_strdup(OPTIONS[i].key); options[j++] = value; } } options[j] = NULL; return 0; } /***********/ static const char sq_mongoose_TAG[] = "Mongoose"; static SQBool show_errors_on_stdout = SQFalse; static void sq_mongoose_release_references(SQ_Mg_Context *self){ SQ_MG_Callback_free(self->master_plugin); SQ_MG_Callback_free(self->master_plugin_exit); SQ_MG_Callback_free(self->user_callback); SQ_MG_Callback_free(self->user_callback_setup); SQ_MG_Callback_free(self->user_callback_exit); } static SQRESULT sq_mongoose_releasehook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) { SQ_Mg_Context *self = (SQ_Mg_Context *)p; if(self){ if(self->ctx){ mg_stop(self->ctx); sq_mongoose_release_references(self); self->ctx = 0; } sq_free(self, sizeof(SQ_Mg_Context)); } return 0; } static SQRESULT sq_mongoose_constructor(HSQUIRRELVM v) { //SQ_FUNC_VARS_NO_TOP(v); SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *)sq_malloc(sizeof(SQ_Mg_Context)); memset(sq_mg_ctx, 0, sizeof(SQ_Mg_Context)); sq_setinstanceup(v, 1, sq_mg_ctx); sq_setreleasehook(v,1, sq_mongoose_releasehook); return 1; } #define GET_mongoose_INSTANCE() \ SQ_Mg_Context *self; \ if((_rc_ = sq_getinstanceup(v,1,(SQUserPointer*)&self,(void*)sq_mongoose_TAG)) < 0) return _rc_; static SQRESULT sq_mongoose_show_errors_on_stdout(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_BOOL(v, 2, onOff); show_errors_on_stdout = onOff; return 0; } static SQRESULT sq_mongoose_modify_passwords_file(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_STRING(v, 2, passwords_file_name); SQ_GET_STRING(v, 3, domain); SQ_GET_STRING(v, 4, user); SQ_GET_STRING(v, 5, password); sq_pushinteger(v, mg_modify_passwords_file(passwords_file_name, domain, user, password)); return 1; } static SQRESULT sq_mongoose_version(HSQUIRRELVM v) { sq_pushstring(v, mg_version(), -1); return 1; } SQInteger blob_write(SQUserPointer file,SQUserPointer p,SQInteger size); // creates a reference dispatching callbacks to squirrel functions static SQRESULT fetchcallback(HSQUIRRELVM v, const char *key, SQ_MG_Callback **sq_cb) { if(!sq_cb) return sq_throwerror(v, "inavlid SQ_MG_Callback parameter value (NULL)"); if(sq_gettype(v, -1) != OT_TABLE) return sq_throwerror(v, "table expected to fetch callbacks"); sq_pushstring(v, key, -1); sq_rawget(v, -2); if (sq_gettype(v, -1) == OT_CLOSURE){ SQBlob b(0, 8192); if(SQ_SUCCEEDED(sq_writeclosure(v,blob_write,&b))) { *sq_cb = SQ_MG_Callback_malloc(b.Len()); if(*sq_cb) memcpy((*sq_cb)->buf, b.GetBuf(), b.Len()); sq_poptop(v); } } else { sq_pop(v, 1); return sq_throwerror(v, "closure expected for callbacks"); } return SQ_OK; } static void * user_callback_proxy(enum mg_event event, struct mg_connection *conn, const struct mg_request_info *ri); // creates a new server using a configuration table static SQRESULT sq_mongoose_start(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_mongoose_INSTANCE(); _rc_ = 1; //assumes succeed const char *options[64]; SQInteger i; if(self->ctx) return sq_throwerror(v, _SC("mongoose already running or stoped incorrectly")); // store the Squirrel vm for use in callback proxies self->v = v; // prepare the mg_config structure from the squirrel table argument memset(options, 0, sizeof (options)); fetchoptions(v, 2, options); fetchcallback(v, "master_plugin", &self->master_plugin); fetchcallback(v, "master_plugin_exit", &self->master_plugin_exit); fetchcallback(v, "user_callback", &self->user_callback); fetchcallback(v, "user_callback_setup", &self->user_callback_setup); fetchcallback(v, "user_callback_exit", &self->user_callback_exit); self->ctx = mg_start(user_callback_proxy, self, options); // free the options string list memory for (i = 0; options[i]; ++i) free((void *) options[i]); // throw an error if the server did not start if (self->ctx == NULL) { sq_mongoose_release_references(self); _rc_ = sq_throwerror(v, "could not start mongoose"); } // return the context so it can be stopped later return _rc_; } static SQRESULT sq_mongoose_stop(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_mongoose_INSTANCE(); if(self->ctx){ mg_stop(self->ctx); sq_mongoose_release_references(self); self->ctx = 0; } return 0; } #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_mongoose_##name,nparams,tycheck} static SQRegFunction sq_mongoose_methods[] = { _DECL_FUNC(constructor, 1, _SC("x")), _DECL_FUNC(start, -1, _SC("x")), _DECL_FUNC(stop, 1, _SC("x")), _DECL_FUNC(modify_passwords_file, 1, _SC("x")), _DECL_FUNC(version, 1, _SC("x")), _DECL_FUNC(show_errors_on_stdout, 2, _SC("xb")), {0,0} }; #undef _DECL_FUNC static SQRESULT sq_mg_url_decode_base(HSQUIRRELVM v, SQInteger is_form_url_encoded) { SQ_FUNC_VARS_NO_TOP(v); GET_mg_context_INSTANCE(); SQ_GET_STRING(v, 2, src); int dst_len = src_size +1; char *dst = sq_getscratchpad(v,dst_len); dst_len = mg_url_decode(src, src_size, dst, dst_len, is_form_url_encoded); sq_pushstring(v, dst, dst_len); return 1; } static SQRESULT sq_mg_url_decode(HSQUIRRELVM v) { return sq_mg_url_decode_base(v, 1); } static SQRESULT sq_mg_uri_decode(HSQUIRRELVM v) { return sq_mg_url_decode_base(v, 0); } static SQRESULT sq_mg_url_encode(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); GET_mg_context_INSTANCE(); SQ_GET_STRING(v, 2, src); char *dst = mg_url_encode(src); sq_pushstring(v, dst, -1); free(dst); return 1; } static SQRESULT sq_mg_md5(HSQUIRRELVM v) { SQ_FUNC_VARS(v); char buf[32 + 1]; unsigned char hash[16]; MD5_CTX ctx; MD5Init(&ctx); for (int i = 2; i <= _top_; ++i) { SQ_GET_STRING(v, i, p); MD5Update(&ctx, (const unsigned char *) p, p_size); } MD5Final(hash, &ctx); mg_bin2str(buf, hash, sizeof(hash)); sq_pushstring(v, buf, -1); return 1; } static SQRESULT sq_mg_debug_print(HSQUIRRELVM v) { SQ_FUNC_VARS(v); SQInteger i, write_count = 0; for (i = 2; i <= _top_; ++i) { sq_tostring(v, i); SQ_GET_STRING(v, -1, value); write_count += fwrite(value, 1, value_size, stderr); sq_poptop(v); } sq_pushinteger(v, write_count); return 1; } #ifdef JNI_ENABLE_LOG #include /* ** A callback for the android_log() SQIntegererface. */ static void jniLog(int iErrCode, const char *zMsg){ __android_log_print(ANDROID_LOG_ERROR,"jniLuaServerLog","(%d) %s\n", iErrCode, zMsg); } static SQRESULT sq_mg_jniLog(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_INTEGER(v, 2, code); SQ_GET_STRING(v, 3, error_message); jniLog(code, error_message); return 0; } #endif #define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_mg_##name,nparams,tycheck} static SQRegFunction sq_mg_methods[] = { _DECL_FUNC(url_decode, 2, _SC(".s")), _DECL_FUNC(uri_decode, 2, _SC(".s")), _DECL_FUNC(url_encode, 2, _SC(".s")), _DECL_FUNC(md5, -2, _SC(".s")), #ifdef JNI_ENABLE_LOG _DECL_FUNC(jniLog, -2, _SC(".s")), #endif _DECL_FUNC(debug_print, -2, _SC(".s")), {0,0} }; #undef _DECL_FUNC static void reg_string(HSQUIRRELVM v, const char *name, const char *val) { sq_pushstring(v, name, -1); if(val) sq_pushstring(v, val, -1); else sq_pushnull(v); sq_rawset(v, -3); } static void reg_integer(HSQUIRRELVM v, const char *name, int val) { sq_pushstring(v, name, -1); sq_pushinteger(v, val); sq_rawset(v, -3); } // pushes request info on the Lua stack as a table static void push_request(HSQUIRRELVM v, const struct mg_request_info *ri) { int i; sq_pushliteral(v, _SC("info")); sq_get(v, -2); #define NEWSLOT_STR(ks) reg_string(v, #ks, ri->ks); NEWSLOT_STR(request_method); NEWSLOT_STR(uri); NEWSLOT_STR(http_version); NEWSLOT_STR(query_string); NEWSLOT_STR(remote_user); NEWSLOT_STR(log_message); #define NEWSLOT_INT(ks) reg_integer(v, #ks, ri->ks); NEWSLOT_INT(remote_ip); NEWSLOT_INT(remote_port); NEWSLOT_INT(status_code); NEWSLOT_INT(is_ssl); #undef NEWSLOT_STR #undef NEWSLOT_INT sq_pushliteral(v, "http_headers"); sq_newtable(v); for (i = 0; i < ri->num_headers; ++i) { reg_string(v, ri->http_headers[i].name, ri->http_headers[i].value); } sq_newslot(v, -3, SQFalse); sq_poptop(v); //remove data table } static SQInteger sq_mg_pcall_master_plugin(HSQUIRRELVM v) { #if 0 SQ_FUNC_VARS_NO_TOP(v); GET_MG_CONNECION(); SQ_GET_STRING(v, 2, func_name); const SQChar *error_message = _SC("No master plugin installed !"); HSQUIRRELVM master_plugin = (HSQUIRRELVM) mg_lock_master_plugin(conn); if(master_plugin){ int master_plugin_saved_top = sq_gettop(master_plugin); //sq_pushcfunction(master_plugin, traceback); /* push traceback function */ //int error_func = sq_gettop(master_plugin); sq_pushstring(master_plugin, func_name, func_name_size); sq_getonroottable(master_plugin); if(sq_gettype(master_plugin, -1) == OT_CLOSURE){ sq_pushroottable(master_plugin); int arg_top = sq_gettop (v); /* Push arguments to dst stack */ int idx = 4; copy_values_between_vms (master_plugin, v, arg_top-idx, idx); if (sq_pcall (master_plugin, arg_top-idx+1, SQTrue, SQTrue) == SQ_OK) { /* run OK? */ int ret_top = sq_gettop (master_plugin); /* Push status = OK */ sq_pushbool (v, SQTrue); /* Return values to src */ //copy_values_between_vms (v, master_plugin, master_plugin_saved_top+2, ret_top); /* pops debug.traceback and result from dst state */ //sq_settop(master_plugin, master_plugin_saved_top); /*unlock master plugin*/ mg_unlock_master_plugin(conn); /* Return true (success) plus return values */ return 1; } error_message = sq_tostring (master_plugin, -1); } else error_message = "Attempt to call an invalid function on master plugin !"; /* pops debug.traceback and result from dst state */ sq_settop(master_plugin, master_plugin_saved_top); /*unlock master plugin*/ mg_unlock_master_plugin(conn); } return sq_throwerror(v, error_message); #endif } static void write_error_message(struct mg_connection *conn, const char * error_msg, SQInteger error_len){ #define PRE_TAG_OPEN "
"
#define PRE_TAG_CLOSE "
" mg_write(conn, PRE_TAG_OPEN, sizeof(PRE_TAG_OPEN)); if(error_len < 0) error_len = strlen(error_msg); mg_write(conn, error_msg, error_len); mg_write(conn, PRE_TAG_CLOSE, sizeof(PRE_TAG_CLOSE)); } #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_SQLite3(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_base64(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_register_mix(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_bloblib(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_iolib(HSQUIRRELVM v); SQUIRREL_API SQInteger sqstd_register_systemlib(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_mathlib(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_Sq_Fpdf(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_sqfs(HSQUIRRELVM v); SQUIRREL_API void sqstd_seterrorhandlers(HSQUIRRELVM v); SQUIRREL_API void sqstd_printcallstack(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif void sq_printfunc(HSQUIRRELVM v,const SQChar *s,...) { va_list vl; va_start(vl, s); vfprintf(stdout, s, vl); va_end(vl); } void sq_errorfunc(HSQUIRRELVM v,const SQChar *s,...) { va_list vl; va_start(vl, s); vfprintf(stderr, s, vl); va_end(vl); } #define INT_CONST(v,num) sq_pushstring(v,_SC(#num),-1);sq_pushinteger(v,num);sq_newslot(v,-3,SQTrue); static HSQUIRRELVM my_new_squirrel(struct mg_context *ctx) { HSQUIRRELVM v = sq_open(1024); if(!v) return 0; sqstd_seterrorhandlers(v); //registers the default error handlers sq_setprintfunc(v, sq_printfunc, sq_errorfunc); //sets the print function sq_pushroottable(v); sqstd_register_bloblib(v); sqstd_register_iolib(v); sqstd_register_systemlib(v); sqstd_register_mathlib(v); sqstd_register_stringlib(v); sqstd_register_base64(v); sqstd_register_Sq_Fpdf(v); sqstd_register_SQLite3(v); sqstd_register_sqfs(v); sq_register_mix(v); sq_pushstring(v,sq_http_request_TAG, -1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(void*)sq_http_request_TAG); sq_insert_reg_funcs(v, mg_http_request_methods); sq_pushstring(v, _SC("info"), -1); sq_newtable(v); sq_newslot(v,-3,SQFalse); sq_newslot(v,-3,SQFalse); sq_insert_reg_funcs(v, sq_mg_methods); sq_pushliteral(v, "APP_ROOT_FOLDER"); sq_pushstring(v, mg_get_option(ctx, "document_root"), -1); sq_newslot(v, -3, SQFalse); sq_poptop(v); //remove roottable return v; } SQInteger blob_read(SQUserPointer file,SQUserPointer p,SQInteger size); SQInteger loadstring(HSQUIRRELVM v, const char * bcode, SQInteger bcode_size) { SQBlob b(0, bcode_size); b.Write(bcode, bcode_size); b.Seek(0, SQ_SEEK_SET); SQInteger rc = sq_readclosure(v, blob_read, &b); return rc < 0 ? rc : 1; } // dispatches a callback to a Lua function if one is registered static void * user_callback_proxy(enum mg_event event, struct mg_connection *conn, const struct mg_request_info *ri) { HSQUIRRELVM v; int e; switch(event){ case MG_NEW_MASTER_PLUGIN: { SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) conn; SQ_MG_Callback *cb = sq_mg_ctx->master_plugin; v = my_new_squirrel((struct mg_context *)ri); if(!v) return 0; if(cb){ if (loadstring(v, cb->buf, cb->size) == 1) { sq_pushroottable(v); if(sq_call(v, 1, SQFalse, SQTrue) != SQ_OK){ sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_poptop(v); //remove function from stack } } return v; } break; case MG_FREE_MASTER_PLUGIN: v = (HSQUIRRELVM)conn; if(v) { SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) ri; SQ_MG_Callback *cb = sq_mg_ctx->master_plugin_exit; if(cb){ if (loadstring(v, cb->buf, cb->size) == 1) { sq_pushroottable(v); if(sq_call(v, 1, SQFalse, SQFalse) != SQ_OK){ sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_poptop(v); //remove function from stack } else sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_close(v); } return 0; break; case MG_NEW_PLUGIN:{ SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) mg_get_user_data(conn); if(!sq_mg_ctx) return 0; SQ_MG_Callback *cb = sq_mg_ctx->user_callback; if (cb) { v = my_new_squirrel((struct mg_context *)ri); if(!v) return 0; SQInteger top = sq_gettop(v); SQ_MG_Callback *cb_setup = sq_mg_ctx->user_callback_setup; if(cb_setup){ if (loadstring(v, cb_setup->buf, cb_setup->size) == 1) { sq_pushroottable(v); if(sq_call(v, 1, SQFalse, SQFalse) != SQ_OK){ sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_poptop(v); //remove function from stack } else sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_pushroottable(v); sq_pushstring(v, sq_mg_user_callback, -1); if (loadstring(v, cb->buf, cb->size) == 1) { sq_newslot(v, -3, SQFalse); } else sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); sq_settop(v, top); //remove everithing left on stack while working here return v; } } return 0; case MG_FREE_PLUGIN: v = (HSQUIRRELVM)mg_get_plugin(conn); if(v){ SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) ri; if(sq_mg_ctx){ SQ_MG_Callback *cb = sq_mg_ctx->user_callback_exit; if(cb){ if (loadstring(v, cb->buf, cb->size) == 1) { sq_pushroottable(v); if(sq_call(v, 1, SQFalse, SQFalse) != SQ_OK){ sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } sq_poptop(v); //remove function from stack } else sq_errorfunc(v, "sq_call failed %d\n%s", __LINE__, sq_getlasterror_str(v)); } } sq_close(v); } return NULL; case MG_INIT_SSL: #ifdef USE_AXTLS //lua_pushstring(L, "MG_INIT_SSL"); *((void**)conn) = SSL_CTX_new(0); return 0; #endif case MG_NEW_REQUEST: case MG_HTTP_ERROR: case MG_EVENT_LOG:{ v = (HSQUIRRELVM) mg_get_plugin(conn); if (v) { SQInteger saved_top = sq_gettop(v); //SQ_Mg_Context *sq_mg_ctx = (SQ_Mg_Context *) mg_get_user_data(conn); sq_pushstring(v, sq_mg_user_callback, -1); if(sq_getonroottable(v) != SQ_OK) { write_error_message(conn, "failed to find mg_user_callback", -1); return NULL; } sq_pushroottable(v); #define CASE(n) case n: sq_pushstring(v, #n, -1);break switch (event) { CASE(MG_INIT_SSL); CASE(MG_HTTP_ERROR); CASE(MG_EVENT_LOG); CASE(MG_NEW_REQUEST); default: sq_pushnull(v); } sq_pushstring(v, sq_http_request_TAG, -1); if(sq_getonroottable(v) == SQ_OK){ sq_pushroottable(v); if(sq_call(v, 1, SQTrue, SQFalse) == SQ_OK){ sq_remove(v, -2); //remove class sq_setinstanceup(v, -1, conn); } } push_request(v, ri); if(sq_call(v, 3, SQTrue, SQFalse) != SQ_OK) { write_error_message(conn, sq_getlasterror_str(v), -1); e = 0; } else { SQBool bval; if(sq_getbool(v, -1, &bval) == SQ_OK) e = bval == SQTrue ? 1 : 0; else e = 0; } sq_settop(v, saved_top); return (void *) e; } } default: return NULL; } return NULL; } #ifdef __cplusplus extern "C" { #endif SQRESULT sqstd_register_mongoose(HSQUIRRELVM v) { sq_insert_reg_funcs(v, sq_mg_methods); sq_pushstring(v,sq_mongoose_TAG, -1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(void*)sq_mongoose_TAG); sq_insert_reg_funcs(v, sq_mongoose_methods); sq_newslot(v,-3,SQTrue); return 1; } #ifdef __cplusplus } #endif