#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 #include #include #include 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) { char32_t *uc; size_t ucsize; int conv_err; if (unicode_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("\n"); printf("\n", cgi("pos")); if (*cgi("globignore")) { printf("\n"); } if (*cgi("globreplace")) { printf("\n"); } printf(""); printf("word_pos); output_attrencoded(msp->misspelled_word); printf("\" />
"); printf("
"); printf("
"); if (msp->word_pos > 30) { p=msp->word_pos-30; for (n=p; nword_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(""); show_part(ispellline+msp->word_pos, strlen(msp->misspelled_word)); printf(""); 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("

"); printf("
"); printf(""); for (isps=msp->first_suggestion; isps; isps=isps->next) { printf("\n", replacelab, isps->suggested_word, isps->suggested_word); replacelab=" "; } printf("\n", replacelab); printf("\n", insertlab); printf("\n", replacealllab); printf("\n"); printf("\n", ignorelab); printf("\n", ignorealllab); printf("
%s%s
%s
%s
%s

%s
%s
"); printf("

"); printf("
"); printf("\n", continuelab); printf("\n", finishlab); printf("
\n"); printf("
\n"); printf("
\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; }