#ifdef _WIN32 #include #pragma warning(disable:4786) #pragma warning(disable:4275) #else extern char **environ; #endif #include #include #include #include #include "fcgi_config.h" #include "fcgiapp.h" #include "aspell.h" static const long MAX_QUERY = 10240; static const long MAX_STDIN = 10240; using namespace std; void parseInput(FCGX_ParamArray envp, FCGX_Stream* in, map* fields); long readContent(FCGX_ParamArray envp, FCGX_Stream* in, char** content); void parseUrlEncoded(map* fields, char* input, size_t inlen); int main() { AspellConfig* sc; AspellCanHaveError* se; AspellSpeller* spell; const AspellWordList* suggestions; AspellStringEnumeration* elements; FCGX_Stream *in, *out, *err; FCGX_ParamArray envp; map fields; map::iterator fi; const char* word, *suggestion; size_t len; int i, j; const char* dictdir, *datadir, *language; int rc = 0; dictdir = getenv("ASPELL_DICT_DIR"); datadir = getenv("ASPELL_DATA_DIR"); language = getenv("ASPELL_LANG"); /* Check if the environment variables have been set */ if ((!dictdir) || (!*dictdir)) { puts("Environment variable ASPELL_DICT_DIR not set."); rc = 1; } if ((!datadir) || (!*datadir)) { puts("Environment variable ASPELL_DATA_DIR not set."); rc = 1; } if ((!language) || (!*language)) { puts("Environment variable ASPELL_LANG not set."); rc = 1; } /* Initialize aspell and create an instance */ if (!rc) { sc = new_aspell_config(); aspell_config_replace(sc, "dict-dir", dictdir); aspell_config_replace(sc, "data-dir", datadir); aspell_config_replace(sc, "lang", language); se = new_aspell_speller(sc); sc = NULL; if (aspell_error_number(se)) { puts(aspell_error_message(se)); delete_aspell_config(sc); rc = 1; } else { spell = to_aspell_speller(se); } } if (!rc) { /* Main application loop */ while (FCGX_Accept(&in, &out, &err, &envp) >= 0) { /* Send response header */ FCGX_PutS("Content-type: text/javascript; charset=UTF-8\n\n", out); FCGX_PutS("data = [", out); /* Parse input */ parseInput(envp, in, &fields); /* Loop around fields (words) */ for (i = 0, fi = fields.begin(); fi != fields.end(); fi++, i++) { if (i) { FCGX_PutS(",", out); } word = fi->second.c_str(); len = strlen(word); /* Check spelling */ if (aspell_speller_check(spell, word, len)) { FCGX_PutS("[1]", out); } else { FCGX_PutS("[0,[", out); /* Loop around suggestions */ suggestions = aspell_speller_suggest(spell, word, len); elements = aspell_word_list_elements(suggestions); j = 0; while ((suggestion = aspell_string_enumeration_next(elements))) { if (j) { FCGX_PutS(",", out); } FCGX_PutS("'", out); FCGX_PutS(suggestion, out); FCGX_PutS("'", out); j++; } delete_aspell_string_enumeration(elements); FCGX_PutS("]]", out); } } /* Clean up and prepare for the next request */ FCGX_PutS("];\n", out); fields.clear(); } } if (!rc) { delete_aspell_speller(spell); delete_aspell_config(sc); } return rc; } void parseInput(FCGX_ParamArray envp, FCGX_Stream* in, map* fields) { char* input, *ctype, *content; long len; /* Get and parse query string */ input = FCGX_GetParam("QUERY_STRING", envp); if (input) { len = strlen(input); if (len > MAX_QUERY) { len = MAX_QUERY; } parseUrlEncoded(fields, input, strlen(input)); } /* Get and parse content */ len = readContent(envp, in, &content); if (len) { ctype = FCGX_GetParam("CONTENT_TYPE", envp); /* Regular form (method=POST) input */ if ((!ctype) || (!strcmp(ctype, "application/x-www-form-urlencoded")) || (!strcmp(ctype, "application/x-url-encoded"))) { parseUrlEncoded(fields, content, len); } delete[] content; } } long readContent(FCGX_ParamArray envp, FCGX_Stream* in, char** content) { char* clenstr = FCGX_GetParam("CONTENT_LENGTH", envp); long len; if (clenstr) { len = strtol(clenstr, &clenstr, 10); /* Don't read more than the predefined limit */ if (len > MAX_STDIN) { len = MAX_STDIN; } *content = new char[len+1]; memset(*content, 0, len+1); len = FCGX_GetStr(*content, len, in); } /* Don't read if CONTENT_LENGTH is missing or if it can't be parsed */ else { *content = 0; len = 0; } /* Chew up whats remaining (required by mod_fastcgi) */ while (FCGX_GetChar(in) != -1); return len; } void parseUrlEncoded(map* fields, char* input, size_t inlen) { size_t n, sep, start, len; char key[8], value[1024], c; start = 0; if (inlen < 1) { return; } for (start = 0; start < inlen; ) { sep = 0; for (n = start; n < inlen; n++) { /* Find pair separator and terminator */ c = input[n]; if (c == '\\') { n++; continue; } if (c == '=') { sep = n ; } else if ((c == ';') || (c == '&')) { break; } } if (sep > 0) { /* Split key form string */ len = sep - start; if (len > 7) { len = 7; } memcpy(key, input + start, len); key[len] = '\0'; /* Split value form string */ sep++; len = n - sep; if (len > 1023) { len = 1023; } memcpy(value, input + sep, len); value[len] = '\0'; /* Add to map */ (*fields)[atoi(key)] = value; } /* Set start position and continue with next one */ start = n + 1; } }