diff options
Diffstat (limited to 'sqwebmail/sqispell.c')
| -rw-r--r-- | sqwebmail/sqispell.c | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/sqwebmail/sqispell.c b/sqwebmail/sqispell.c new file mode 100644 index 0000000..e695353 --- /dev/null +++ b/sqwebmail/sqispell.c @@ -0,0 +1,677 @@ +#include "config.h" +/* +** Copyright 1998 - 2001 Double Precision, Inc. See COPYING for +** distribution information. +*/ + + +/* +*/ +#include "sqwebmail.h" +#include "maildir.h" +#include "folder.h" +#include "cgi/cgi.h" +#include "rfc2045/rfc2045.h" +#include "maildir/maildirmisc.h" +#include "buf.h" +#include "ispell.h" +#include "filter.h" +#include "newmsg.h" +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> + +extern const char *sqwebmail_content_charset; +extern void output_form(const char *); +extern const char *sqwebmail_content_ispelldict; +extern void output_attrencoded(const char *); + +static void spelladd(const char *); +static int search_spell(const char *, unsigned, unsigned); + +int spell_start(const char *c) +{ +char *filename=maildir_find(INBOX "." DRAFTS, c); + + if (!c) return (-1); + + if (search_spell(filename, 0, 0) == 0) + return (-1); + + return (0); +} + +/* +** Search for misspelled words. +*/ + +static struct rfc2045 *findtext(struct rfc2045 *); + +static char *spell_check(const char *, unsigned, unsigned, + const char *, const char *, const char *, int *); + +static int search_spell(const char *filename, unsigned parnum, unsigned pos) +{ +struct rfc2045 *rfcp, *textp; +struct buf newtext, current_line; +off_t start_pos, end_pos, start_body; +int made_replacements, has_misspelling; +char *new_line; +unsigned paragraph; +const char *ignoreword=""; +const char *replacefrom=""; +const char *replaceto=""; +int checked=0; +off_t dummy; +FILE *fp=0; +int x; + + x=maildir_safeopen(filename, O_RDONLY, 0); + if (x >= 0) + if ((fp=fdopen(x, "r")) == 0) + close(x); + + if (!fp) return (0); + rfcp=rfc2045_fromfp(fp); + if (!rfcp) enomem(); + + textp=findtext(rfcp); + + if (!textp) + { + rfc2045_free(rfcp); + fclose(fp); + return (0); + } + + buf_init(&newtext); + buf_init(¤t_line); + + rfc2045_mimepos(textp, &start_pos, &end_pos, &start_body, + &dummy, &dummy); + if (fseek(fp, start_body, SEEK_SET) == -1) + enomem(); + + made_replacements=0; + has_misspelling=0; + paragraph=0; + for ( ; start_body < end_pos; start_body++) + { + int c=getc(fp); + + if (c < 0) enomem(); + if (c != '\n') + { + buf_append(¤t_line, c); + continue; + } + buf_append(¤t_line, '\0'); + if (parnum) + { + --parnum; + buf_cat(&newtext, current_line.ptr); + buf_cat(&newtext, "\n"); + current_line.cnt=0; + ++paragraph; + continue; + } + + if (!checked) + { + int l; + + checked=1; + if ((l=strlen(cgi("word"))) > 0) + { + +/* Ok, what should we do? */ + + const char *newword=cgi("REPLACE"); + + if (!*newword || strcmp(newword, "#other") == 0) + newword=cgi("OTHER"); + /* + ** Perhaps they entered the word without + ** checking this checkmark. + */ + else if (*newword == '#') + newword=""; + + if (*newword && pos + l <= strlen(current_line.ptr)) + { + struct buf tempbuf; + + buf_init(&tempbuf); + buf_cpyn(&tempbuf, current_line.ptr, + pos); + buf_cat(&tempbuf, newword); + buf_cat(&tempbuf, + current_line.ptr+pos+l); + pos += strlen(newword); + if (*cgi("REPLACEALL")) + { + replacefrom=cgi("word"); + replaceto=newword; + } + buf_append(&tempbuf, '\0'); + buf_cpy(¤t_line, tempbuf.ptr); + buf_append(¤t_line, '\0'); + buf_free(&tempbuf); + made_replacements=1; + } + else + { + pos += l; + if (strcmp(cgi("REPLACE"), + "#ignoreall") == 0) + ignoreword=cgi("word"); + } + + if (strcmp(cgi("REPLACE"), + "#insert") == 0) + { + spelladd(cgi("word")); + } + } + } + + + if (*current_line.ptr == '>') + { + buf_cat(&newtext, current_line.ptr); + buf_cat(&newtext, "\n"); + pos=0; + current_line.cnt=0; + ++paragraph; + continue; + } + if (!has_misspelling) + { + new_line=spell_check(current_line.ptr, paragraph, pos, + ignoreword, replacefrom, replaceto, + &has_misspelling); + if (new_line) + { + buf_cat(&newtext, new_line); + free(new_line); + made_replacements=1; + } + else buf_cat(&newtext, current_line.ptr); + } + else buf_cat(&newtext, current_line.ptr); + buf_cat(&newtext, "\n"); + pos=0; + current_line.cnt=0; + ++paragraph; + } + if (current_line.cnt) + buf_cat(&newtext, "\n"); + rfc2045_free(rfcp); + fclose(fp); + if (made_replacements) + { + char *p=newmsg_createdraft_do(filename, newtext.ptr, + NEWMSG_SQISPELL); + + if (p) free(p); + + if (*cgi("error")) + { + has_misspelling=0; /* Abort spell checking */ + } + } + + buf_free(&newtext); + buf_free(¤t_line); + + if (*ignoreword) + { + static char *p=0; + + if (p) free(p); + p=malloc(strlen(cgi("globignore")) + 2 + strlen(ignoreword)); + + if (!p) enomem(); + + strcpy(p, cgi("globignore")); + if (*p) strcat(p, ":"); + strcat(p, ignoreword); + cgi_put("globignore", p); + } + + if (*replacefrom) + { + static char *p=0; + + if (p) free(p); + p=malloc(strlen(cgi("globreplace"))+3 + +strlen(replacefrom)+strlen(replaceto)); + + if (!p) enomem(); + strcpy(p, cgi("globreplace")); + if (*p) strcat(p, ":"); + strcat(strcat(strcat(p, replacefrom), ":"), replaceto); + cgi_put("globreplace", p); + free(p); + } + if (has_misspelling) return (1); + return (0); +} + +static struct rfc2045 *findtext(struct rfc2045 *rfcp) +{ +struct rfc2045 *textp; +const char *content_type; +const char *content_transfer_encoding; +const char *charset; + + rfc2045_mimeinfo(rfcp, &content_type, + &content_transfer_encoding, &charset); + if (strncmp(content_type, "text/", 5) == 0) + textp=rfcp; + else + { + for (textp=rfcp->firstpart; textp; textp=textp->next) + { + if (textp->isdummy) continue; + rfc2045_mimeinfo(textp, &content_type, + &content_transfer_encoding, &charset); + if (strncmp(content_type, "text/", 5) == 0) + break; + } + } + return (textp); +} + +/* +** Ok, check a single paragraph, starting at position #pos. +** +** If some replacements were made due to previous saved 'replace all' words, +** return the text of the modified line. Otherwise return NULL. +** +** Set *hasmisspelled to 1 if there are some misspellings in this line. +*/ + +static struct ispell *ispellptr; +static char *ispellline=0; +static unsigned paragraph; + +static int spellignore(const char *); +static char *spellreplace(const char *); + +static char *spell_check(const char *line, unsigned pnum, unsigned pos, + const char *ignoreword, + const char *replacefrom, + const char *replaceto, + int *hasmisspelled) +{ +struct ispell_misspelled *msp, *np; +char *newline=0; +const char *newword; +char *w; + + if (strlen(line) <= pos) return (0); /* Sanity check */ + + ispellptr=ispell_run(sqwebmail_content_ispelldict, line+pos); + if (!ispellptr) enomem(); + for (msp=ispellptr->first_misspelled; msp; msp=msp->next) + if (msp->misspelled_word) + msp->word_pos += pos; + + for (msp=ispellptr->first_misspelled; msp; msp=msp->next) + { + if ((*ignoreword && + strcmp(msp->misspelled_word, ignoreword) == 0) + || spellignore(msp->misspelled_word)) + { + msp->misspelled_word=0; + continue; + } + + newword=0; + if ( *replacefrom && + strcmp(msp->misspelled_word, replacefrom) == 0) + newword=replaceto; + + w=0; + if (newword || + (newword=w=spellreplace(msp->misspelled_word)) != 0) + { + char *p=malloc(strlen(newline ? newline:line)+strlen(newword)+1); + + if (!p) enomem(); + memcpy(p, (newline ? newline:line), msp->word_pos); + strcpy(p+msp->word_pos, newword); + strcat(p, (newline ? newline:line)+msp->word_pos+ + strlen(msp->misspelled_word)); + if (newline) free(newline); + newline=p; + for (np=msp; (np=np->next) != 0; ) + np->word_pos += strlen(newword)-strlen(msp->misspelled_word); + msp->misspelled_word=0; + if (w) + free(w); + continue; + } + *hasmisspelled=1; + paragraph=pnum; + break; + } + if (!hasmisspelled) + { + ispell_free(ispellptr); + ispellptr=0; + } + else + { + if (ispellline) free(ispellline); + if ((ispellline=malloc(strlen( newline ? newline:line)+1)) == 0) + enomem(); + strcpy(ispellline, newline ? newline:line); + } + return (newline); +} + +static void showfunc(const char *p, size_t n, void *dummy) +{ + while (n) + { + if (*p == ' ') + printf(" "); + else if (*p != '\n') + putchar(*p); + p++; + --n; + } +} + +static void show_part(const char *ptr, size_t cnt) +{ + unicode_char *uc; + size_t ucsize; + int conv_err; + + if (libmail_u_convert_tou_tobuf(ptr, cnt, + sqwebmail_content_charset, + &uc, + &ucsize, + &conv_err) == 0) + { + if (conv_err) + { + free(uc); + uc=NULL; + } + } + + + if (uc) + { + + struct filter_info info; + + filter_start(&info, sqwebmail_content_charset, + &showfunc, NULL); + filter(&info, uc, ucsize); + filter_end(&info); + + free(uc); + } +} + +void spell_show() +{ +const char *draftmessage=cgi("draftmessage"); +struct ispell_misspelled *msp; +struct ispell_suggestion *isps; +size_t p, l=strlen(ispellline), n; +const char *ignorelab=getarg("IGNORE"); +const char *ignorealllab=getarg("IGNOREALL"); +const char *replacelab=getarg("REPLACE"); +const char *replacealllab=getarg("REPLACEALL"); +const char *insertlab=getarg("INSERT"); +const char *continuelab=getarg("CONTINUE"); +const char *finishlab=getarg("FINISH"); + + if (!ispellptr) enomem(); + + if (!ignorelab) ignorelab=""; + if (!ignorealllab) ignorealllab=""; + if (!replacelab) replacelab=""; + if (!replacealllab) replacealllab=""; + if (!continuelab) continuelab=""; + if (!finishlab) finishlab=""; + + for (msp=ispellptr->first_misspelled; msp; msp=msp->next) + if (msp->misspelled_word) break; + if (!msp) enomem(); + + CHECKFILENAME(draftmessage); + + printf("<input type=\"hidden\" name=\"form\" value=\"spellchk\" />\n"); + printf("<input type=\"hidden\" name=\"pos\" value=\"%s\" />\n", cgi("pos")); + if (*cgi("globignore")) + { + printf("<input type=\"hidden\" name=\"globignore\" value=\""); + output_attrencoded(cgi("globignore")); + printf("\" />\n"); + } + if (*cgi("globreplace")) + { + printf("<input type=\"hidden\" name=\"globreplace\" value=\""); + output_attrencoded(cgi("globreplace")); + printf("\" />\n"); + } + + printf("<input type=\"hidden\" name=\"draftmessage\" value=\""); + output_attrencoded(draftmessage); + printf("\" />"); + printf("<input type=\"hidden\" name=\"row\" value=\"%u\" /><input type=\"hidden\" name=\"col\" value=\"%u\" /><input type=\"hidden\" name=\"word\" value=\"", + (unsigned)paragraph, + (unsigned)msp->word_pos); + output_attrencoded(msp->misspelled_word); + + printf("\" /><table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" " + "class=\"box-small-outer\"><tr><td>"); + printf("<table border=\"0\" cellspacing=\"0\" class=\"spellcheck-background\"><tr><td>"); + + printf("<table border=\"1\" cellspacing=\"0\" cellpadding=\"8\" class=\"spellcheck-excerpt\"><tr><td align=\"center\"><span style=\"color: #000000\" class=\"spellcheck-excerpt\">"); + + if (msp->word_pos > 30) + { + p=msp->word_pos-30; + for (n=p; n<msp->word_pos; n++) + if (ispellline[n] == ' ') + { + while (n < p && ispellline[n] == ' ') + ++n; + p=n; + break; + } + printf("... "); + } + else + p=0; + + + show_part(ispellline+p, msp->word_pos-p); + printf("<strong>"); + show_part(ispellline+msp->word_pos, strlen(msp->misspelled_word)); + printf("</strong>"); + + p=msp->word_pos+strlen(msp->misspelled_word); + if (l-p < 30) + { + n=l-p; + } + else n=30; + + while (n) + { + if (ispellline[n+p] != ' ') + { + --n; + continue; + } + while (n && ispellline[n+p-1] == ' ') + --n; + break; + } + + show_part(ispellline+p, n); + + if (n != l-p) + printf(" ..."); + printf("</span></td></tr></table><br />"); + printf("<table border=\"1\" cellpadding=\"8\" class=\"spellcheck-main\"><tr><td>"); + + printf("<table border=\"0\">"); + for (isps=msp->first_suggestion; isps; isps=isps->next) + { + printf("<tr><td>%s</td><td><input type=\"radio\" name=\"REPLACE\" value=\"%s\" /></td><td>%s</td></tr>\n", + replacelab, + isps->suggested_word, + isps->suggested_word); + replacelab=" "; + } + printf("<tr><td>%s</td><td><input type=\"radio\" name=\"REPLACE\" value=\"#other\" /></td><td><input type=\"text\" name=\"OTHER\" size=\"20\" /></td></tr>\n", + replacelab); + printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#insert\" /></td><td>%s</td></tr>\n", + insertlab); + + printf("<tr><td> </td><td><input type=\"checkbox\" name=\"REPLACEALL\" /></td><td>%s</td></tr>\n", + replacealllab); + printf("<tr><td> </td><td colspan=\"2\"><hr width=\"100%%\" /></td></tr>\n"); + printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#ignore\" /></td><td>%s</td></tr>\n", + ignorelab); + printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#ignoreall\" /></td><td>%s</td></tr>\n", + ignorealllab); + printf("</table>"); + printf("</td></tr></table><br />"); + printf("<table border=\"1\" cellpadding=\"8\" class=\"spellcheck-continue\"><tr><td>"); + printf("<input type=\"submit\" name=\"continue\" value=\"%s\" />\n", + continuelab); + printf("<input type=\"submit\" name=\"finish\" value=\"%s\" />\n", + finishlab); + printf("</td></tr></table>\n"); + printf("</td></tr></table>\n"); + printf("</td></tr></table>\n"); +} + +static FILE *opendict(const char *mode) +{ +FILE *fp; +char *p=malloc(sqwebmail_content_ispelldict ? + strlen(sqwebmail_content_ispelldict)+20:20); + + if (!p) enomem(); + strcat(strcpy(p, sqwebmail_content_ispelldict ? + "sqwebmail-dict-":"sqwebmail-dict"), + sqwebmail_content_ispelldict ? sqwebmail_content_ispelldict:""); + fp=fopen(p, mode); + free(p); + return (fp); +} + +static int spellignore(const char *word) +{ +char buf[100]; +const char *c; +char *p, *q; +FILE *fp=opendict("r"); + + if (!fp) return (0); + while (fgets(buf, sizeof(buf), fp) != NULL) + { + if ((p=strchr(buf, '\n')) != 0) *p=0; + if (strcmp(word, buf) == 0) + { + fclose(fp); + return (1); + } + } + fclose(fp); + + c=cgi("globignore"); + + p=malloc(strlen(c)+1); + if (!p) enomem(); + strcpy(p, c); + + for (q=p; (q=strtok(q, ":")) != 0; q=0) + if (strcmp(q, word) == 0) + { + free(p); + return (1); + } + + return (0); +} + +static void spelladd(const char *word) +{ +FILE *fp=opendict("a"); + + if (fp) + { + fprintf(fp, "%s\n", word); + fclose(fp); + } +} + +static char *spellreplace(const char *word) +{ +char *p, *q, *r; +const char *c=cgi("globreplace"); + + p=malloc(strlen(c)+1); + if (!p) enomem(); + strcpy(p, c); + for (q=p; (q=strtok(q, ":")) != 0 && (r=strtok(0, ":")) != 0; q=0) + { + if (strcmp(q, word) == 0) + { + q=malloc(strlen(r)+1); + if (!q) enomem(); + strcpy(q, r); + free(p); + return (q); + } + } + free(p); + return (0); +} + +void spell_check_continue() +{ +const char *filename=cgi("draftmessage"); +unsigned parnum=atol(cgi("row")); +unsigned pos=atol(cgi("col")); +char *draftfilename; + + CHECKFILENAME(filename); + draftfilename=maildir_find(INBOX "." DRAFTS, filename); + if (!draftfilename) + { + output_form("folder.html"); + return; + } + + if (search_spell(draftfilename, parnum, pos) && + *cgi("continue")) + output_form("spellchk.html"); + else + { + cgi_put("draft", cgi("draftmessage")); + cgi_put("previewmsg","SPELLCHK"); + output_form("newmsg.html"); + } + free(draftfilename); +} + +void ispell_cleanup() +{ + if(ispellline) free(ispellline); + ispellline=NULL; +} |
