Back to index

courier  0.68.2
sqispell.c
Go to the documentation of this file.
00001 #include "config.h"
00002 /*
00003 ** Copyright 1998 - 2001 Double Precision, Inc.  See COPYING for
00004 ** distribution information.
00005 */
00006 
00007 
00008 /*
00009 */
00010 #include      "sqwebmail.h"
00011 #include      "maildir.h"
00012 #include      "folder.h"
00013 #include      "cgi/cgi.h"
00014 #include      "rfc2045/rfc2045.h"
00015 #include      "maildir/maildirmisc.h"
00016 #include      "buf.h"
00017 #include      "ispell.h"
00018 #include      "filter.h"
00019 #include      "newmsg.h"
00020 #include      <stdio.h>
00021 #include      <string.h>
00022 #include      <fcntl.h>
00023 #include      <ctype.h>
00024 
00025 extern const char *sqwebmail_content_charset;
00026 extern void output_form(const char *);
00027 extern const char *sqwebmail_content_ispelldict;
00028 extern void output_attrencoded(const char *);
00029 
00030 static void spelladd(const char *);
00031 static int search_spell(const char *, unsigned, unsigned);
00032 
00033 int spell_start(const char *c)
00034 {
00035 char   *filename=maildir_find(INBOX "." DRAFTS, c);
00036 
00037        if (!c)       return (-1);
00038 
00039        if (search_spell(filename, 0, 0) == 0)
00040               return (-1);
00041 
00042        return (0);
00043 }
00044 
00045 /*
00046 ** Search for misspelled words.
00047 */
00048 
00049 static struct rfc2045 *findtext(struct rfc2045 *);
00050 
00051 static char *spell_check(const char *, unsigned, unsigned,
00052        const char *, const char *, const char *, int *);
00053 
00054 static int search_spell(const char *filename, unsigned parnum, unsigned pos)
00055 {
00056 struct rfc2045       *rfcp, *textp;
00057 struct buf newtext, current_line;
00058 off_t start_pos, end_pos, start_body;
00059 int    made_replacements, has_misspelling;
00060 char *new_line;
00061 unsigned paragraph;
00062 const char    *ignoreword="";
00063 const char    *replacefrom="";
00064 const char    *replaceto="";
00065 int    checked=0;
00066 off_t  dummy;
00067 FILE   *fp=0;
00068 int    x;
00069 
00070        x=maildir_safeopen(filename, O_RDONLY, 0);
00071        if (x >= 0)
00072               if ((fp=fdopen(x, "r")) == 0)
00073                      close(x);
00074 
00075        if (!fp)      return (0);
00076        rfcp=rfc2045_fromfp(fp);
00077        if (!rfcp)    enomem();
00078 
00079        textp=findtext(rfcp);
00080 
00081        if (!textp)
00082        {
00083               rfc2045_free(rfcp);
00084               fclose(fp);
00085               return (0);
00086        }
00087 
00088        buf_init(&newtext);
00089        buf_init(&current_line);
00090 
00091         rfc2045_mimepos(textp, &start_pos, &end_pos, &start_body,
00092               &dummy, &dummy);
00093         if (fseek(fp, start_body, SEEK_SET) == -1)
00094                 enomem();
00095 
00096        made_replacements=0;
00097        has_misspelling=0;
00098        paragraph=0;
00099         for ( ; start_body < end_pos; start_body++)
00100        {
00101        int    c=getc(fp);
00102 
00103               if (c < 0)    enomem();
00104               if (c != '\n')
00105               {
00106                      buf_append(&current_line, c);
00107                      continue;
00108               }
00109               buf_append(&current_line, '\0');
00110               if (parnum)
00111               {
00112                      --parnum;
00113                      buf_cat(&newtext, current_line.ptr);
00114                      buf_cat(&newtext, "\n");
00115                      current_line.cnt=0;
00116                      ++paragraph;
00117                      continue;
00118               }
00119 
00120               if (!checked)
00121               {
00122               int    l;
00123 
00124                      checked=1;
00125                      if ((l=strlen(cgi("word"))) > 0)
00126                      {
00127 
00128 /* Ok, what should we do? */
00129 
00130                      const char *newword=cgi("REPLACE");
00131 
00132                             if (!*newword || strcmp(newword, "#other") == 0)
00133                                    newword=cgi("OTHER");
00134                             /*
00135                             ** Perhaps they entered the word without
00136                             ** checking this checkmark.
00137                             */
00138                             else if (*newword == '#')
00139                                    newword="";
00140 
00141                             if (*newword && pos + l <= strlen(current_line.ptr))
00142                             {
00143                             struct buf tempbuf;
00144 
00145                                    buf_init(&tempbuf);
00146                                    buf_cpyn(&tempbuf, current_line.ptr,
00147                                           pos);
00148                                    buf_cat(&tempbuf, newword);
00149                                    buf_cat(&tempbuf,
00150                                           current_line.ptr+pos+l);
00151                                    pos += strlen(newword);
00152                                    if (*cgi("REPLACEALL"))
00153                                    {
00154                                           replacefrom=cgi("word");
00155                                           replaceto=newword;
00156                                    }
00157                                    buf_append(&tempbuf, '\0');
00158                                    buf_cpy(&current_line, tempbuf.ptr);
00159                                    buf_append(&current_line, '\0');
00160                                    buf_free(&tempbuf);
00161                                    made_replacements=1;
00162                             }
00163                             else
00164                             {
00165                                    pos += l;
00166                                    if (strcmp(cgi("REPLACE"),
00167                                           "#ignoreall") == 0)
00168                                           ignoreword=cgi("word");
00169                             }
00170 
00171                             if (strcmp(cgi("REPLACE"),
00172                                           "#insert") == 0)
00173                             {
00174                                    spelladd(cgi("word"));
00175                             }
00176                      }
00177               }
00178 
00179 
00180               if (*current_line.ptr == '>')
00181               {
00182                      buf_cat(&newtext, current_line.ptr);
00183                      buf_cat(&newtext, "\n");
00184                      pos=0;
00185                      current_line.cnt=0;
00186                      ++paragraph;
00187                      continue;
00188               }
00189               if (!has_misspelling)
00190               {
00191                      new_line=spell_check(current_line.ptr, paragraph, pos,
00192                             ignoreword, replacefrom, replaceto,
00193                             &has_misspelling);
00194                      if (new_line)
00195                      {
00196                             buf_cat(&newtext, new_line);
00197                             free(new_line);
00198                             made_replacements=1;
00199                      }
00200                      else   buf_cat(&newtext, current_line.ptr);
00201               }
00202               else   buf_cat(&newtext, current_line.ptr);
00203               buf_cat(&newtext, "\n");
00204               pos=0;
00205               current_line.cnt=0;
00206               ++paragraph;
00207        }
00208        if (current_line.cnt)
00209               buf_cat(&newtext, "\n");
00210        rfc2045_free(rfcp);
00211        fclose(fp);
00212        if (made_replacements)
00213        {
00214        char   *p=newmsg_createdraft_do(filename, newtext.ptr,
00215                                     NEWMSG_SQISPELL);
00216 
00217               if (p) free(p);
00218 
00219               if (*cgi("error"))
00220               {
00221                      has_misspelling=0;   /* Abort spell checking */
00222               }
00223        }
00224 
00225        buf_free(&newtext);
00226        buf_free(&current_line);
00227 
00228        if (*ignoreword)
00229        {
00230        static char *p=0;
00231 
00232               if (p) free(p);
00233               p=malloc(strlen(cgi("globignore")) + 2 + strlen(ignoreword));
00234 
00235               if (!p)       enomem();
00236 
00237               strcpy(p, cgi("globignore"));
00238               if (*p)       strcat(p, ":");
00239               strcat(p, ignoreword);
00240               cgi_put("globignore", p);
00241        }
00242 
00243        if (*replacefrom)
00244        {
00245        static char *p=0;
00246 
00247               if (p) free(p);
00248               p=malloc(strlen(cgi("globreplace"))+3
00249                      +strlen(replacefrom)+strlen(replaceto));
00250 
00251               if (!p)       enomem();
00252               strcpy(p, cgi("globreplace"));
00253               if (*p)       strcat(p, ":");
00254               strcat(strcat(strcat(p, replacefrom), ":"), replaceto);
00255               cgi_put("globreplace", p);
00256               free(p);
00257        }
00258        if (has_misspelling) return (1);
00259        return (0);
00260 }
00261 
00262 static struct rfc2045 *findtext(struct rfc2045 *rfcp)
00263 {
00264 struct rfc2045 *textp;
00265 const char *content_type;
00266 const char *content_transfer_encoding;
00267 const char *charset;
00268 
00269        rfc2045_mimeinfo(rfcp, &content_type,
00270               &content_transfer_encoding, &charset);
00271        if (strncmp(content_type, "text/", 5) == 0)
00272               textp=rfcp;
00273        else
00274        {
00275               for (textp=rfcp->firstpart; textp; textp=textp->next)
00276               {
00277                      if (textp->isdummy)  continue;
00278                      rfc2045_mimeinfo(textp, &content_type,
00279                             &content_transfer_encoding, &charset);
00280                      if (strncmp(content_type, "text/", 5) == 0)
00281                             break;
00282               }
00283        }
00284        return (textp);
00285 }
00286 
00287 /*
00288 ** Ok, check a single paragraph, starting at position #pos.
00289 **
00290 ** If some replacements were made due to previous saved 'replace all' words,
00291 ** return the text of the modified line.  Otherwise return NULL.
00292 **
00293 ** Set *hasmisspelled to 1 if there are some misspellings in this line.
00294 */
00295 
00296 static struct ispell *ispellptr;
00297 static char *ispellline=0;
00298 static unsigned paragraph;
00299 
00300 static int spellignore(const char *);
00301 static char *spellreplace(const char *);
00302 
00303 static char *spell_check(const char *line, unsigned pnum, unsigned pos,
00304        const char *ignoreword,
00305        const char *replacefrom,
00306        const char *replaceto,
00307        int *hasmisspelled)
00308 {
00309 struct ispell_misspelled    *msp, *np;
00310 char   *newline=0;
00311 const char *newword;
00312 char   *w;
00313 
00314        if (strlen(line) <= pos)    return (0);   /* Sanity check */
00315 
00316        ispellptr=ispell_run(sqwebmail_content_ispelldict, line+pos);
00317        if (!ispellptr)      enomem();
00318        for (msp=ispellptr->first_misspelled; msp; msp=msp->next)
00319               if (msp->misspelled_word)
00320                      msp->word_pos += pos;
00321 
00322        for (msp=ispellptr->first_misspelled; msp; msp=msp->next)
00323        {
00324               if ((*ignoreword &&
00325                      strcmp(msp->misspelled_word, ignoreword) == 0)
00326                      || spellignore(msp->misspelled_word))
00327               {
00328                      msp->misspelled_word=0;
00329                      continue;
00330               }
00331 
00332               newword=0;
00333               if ( *replacefrom &&
00334                      strcmp(msp->misspelled_word, replacefrom) == 0)
00335                      newword=replaceto;
00336 
00337               w=0;
00338               if (newword ||
00339                      (newword=w=spellreplace(msp->misspelled_word)) != 0)
00340               {
00341               char   *p=malloc(strlen(newline ? newline:line)+strlen(newword)+1);
00342 
00343                      if (!p)       enomem();
00344                      memcpy(p, (newline ? newline:line), msp->word_pos);
00345                      strcpy(p+msp->word_pos, newword);
00346                      strcat(p, (newline ? newline:line)+msp->word_pos+
00347                             strlen(msp->misspelled_word));
00348                      if (newline)  free(newline);
00349                      newline=p;
00350                      for (np=msp; (np=np->next) != 0; )
00351                             np->word_pos += strlen(newword)-strlen(msp->misspelled_word);
00352                      msp->misspelled_word=0;
00353                      if (w)
00354                             free(w);
00355                      continue;
00356               }
00357               *hasmisspelled=1;
00358               paragraph=pnum;
00359               break;
00360        }
00361        if (!hasmisspelled)
00362        {
00363               ispell_free(ispellptr);
00364               ispellptr=0;
00365        }
00366        else
00367        {
00368               if (ispellline)      free(ispellline);
00369               if ((ispellline=malloc(strlen( newline ? newline:line)+1)) == 0)
00370                      enomem();
00371               strcpy(ispellline, newline ? newline:line);
00372        }
00373        return (newline);
00374 }
00375 
00376 static void showfunc(const char *p, size_t n, void *dummy)
00377 {
00378        while (n)
00379        {
00380               if (*p == ' ')
00381                      printf("&nbsp;");
00382               else if (*p != '\n')
00383                      putchar(*p);
00384               p++;
00385               --n;
00386        }
00387 }
00388 
00389 static void show_part(const char *ptr, size_t cnt)
00390 {
00391        unicode_char *uc;
00392        size_t ucsize;
00393        int conv_err;
00394 
00395        if (libmail_u_convert_tou_tobuf(ptr, cnt,
00396                                    sqwebmail_content_charset,
00397                                    &uc,
00398                                    &ucsize,
00399                                    &conv_err) == 0)
00400        {
00401               if (conv_err)
00402               {
00403                      free(uc);
00404                      uc=NULL;
00405               }
00406        }
00407 
00408 
00409        if (uc)
00410        {
00411 
00412               struct filter_info info;
00413 
00414               filter_start(&info, sqwebmail_content_charset,
00415                           &showfunc, NULL);
00416               filter(&info, uc, ucsize);
00417               filter_end(&info);
00418 
00419               free(uc);
00420        }
00421 }
00422 
00423 void spell_show()
00424 {
00425 const char *draftmessage=cgi("draftmessage");
00426 struct ispell_misspelled *msp;
00427 struct ispell_suggestion *isps;
00428 size_t p, l=strlen(ispellline), n;
00429 const char *ignorelab=getarg("IGNORE");
00430 const char *ignorealllab=getarg("IGNOREALL");
00431 const char *replacelab=getarg("REPLACE");
00432 const char *replacealllab=getarg("REPLACEALL");
00433 const char *insertlab=getarg("INSERT");
00434 const char *continuelab=getarg("CONTINUE");
00435 const char *finishlab=getarg("FINISH");
00436 
00437        if (!ispellptr)      enomem();
00438 
00439        if (!ignorelab)             ignorelab="";
00440        if (!ignorealllab)   ignorealllab="";
00441        if (!replacelab)     replacelab="";
00442        if (!replacealllab)  replacealllab="";
00443        if (!continuelab)    continuelab="";
00444        if (!finishlab)             finishlab="";
00445 
00446        for (msp=ispellptr->first_misspelled; msp; msp=msp->next)
00447               if (msp->misspelled_word)   break;
00448        if (!msp)     enomem();
00449 
00450        CHECKFILENAME(draftmessage);
00451 
00452        printf("<input type=\"hidden\" name=\"form\" value=\"spellchk\" />\n");
00453        printf("<input type=\"hidden\" name=\"pos\" value=\"%s\" />\n", cgi("pos"));
00454        if (*cgi("globignore"))
00455        {
00456               printf("<input type=\"hidden\" name=\"globignore\" value=\"");
00457               output_attrencoded(cgi("globignore"));
00458               printf("\" />\n");
00459        }
00460        if (*cgi("globreplace"))
00461        {
00462               printf("<input type=\"hidden\" name=\"globreplace\" value=\"");
00463               output_attrencoded(cgi("globreplace"));
00464               printf("\" />\n");
00465        }
00466 
00467        printf("<input type=\"hidden\" name=\"draftmessage\" value=\"");
00468        output_attrencoded(draftmessage);
00469        printf("\" />");
00470        printf("<input type=\"hidden\" name=\"row\" value=\"%u\" /><input type=\"hidden\" name=\"col\" value=\"%u\" /><input type=\"hidden\" name=\"word\" value=\"",
00471               (unsigned)paragraph,
00472               (unsigned)msp->word_pos);
00473        output_attrencoded(msp->misspelled_word);
00474 
00475        printf("\" /><table border=\"0\" cellspacing=\"0\" cellpadding=\"1\" "
00476               "class=\"box-small-outer\"><tr><td>");
00477        printf("<table border=\"0\" cellspacing=\"0\" class=\"spellcheck-background\"><tr><td>");
00478 
00479        printf("<table border=\"1\" cellspacing=\"0\" cellpadding=\"8\" class=\"spellcheck-excerpt\"><tr><td align=\"center\"><span style=\"color: #000000\" class=\"spellcheck-excerpt\">");
00480 
00481        if (msp->word_pos > 30)
00482        {
00483               p=msp->word_pos-30;
00484               for (n=p; n<msp->word_pos; n++)
00485                      if (ispellline[n] == ' ')
00486                      {
00487                             while (n < p && ispellline[n] == ' ')
00488                                    ++n;
00489                             p=n;
00490                             break;
00491                      }
00492               printf("...&nbsp;");
00493        }
00494        else
00495               p=0;
00496 
00497 
00498        show_part(ispellline+p, msp->word_pos-p);
00499        printf("<strong>");
00500        show_part(ispellline+msp->word_pos, strlen(msp->misspelled_word));
00501        printf("</strong>");
00502 
00503        p=msp->word_pos+strlen(msp->misspelled_word);
00504        if (l-p < 30)
00505        {
00506               n=l-p;
00507        }
00508        else   n=30;
00509 
00510        while (n)
00511        {
00512               if (ispellline[n+p] != ' ')
00513               {
00514                      --n;
00515                      continue;
00516               }
00517               while (n && ispellline[n+p-1] == ' ')
00518                      --n;
00519               break;
00520        }
00521 
00522        show_part(ispellline+p, n);
00523 
00524        if (n != l-p)
00525               printf("&nbsp;...");
00526        printf("</span></td></tr></table><br />");
00527        printf("<table border=\"1\" cellpadding=\"8\" class=\"spellcheck-main\"><tr><td>");
00528 
00529        printf("<table border=\"0\">");
00530        for (isps=msp->first_suggestion; isps; isps=isps->next)
00531        {
00532               printf("<tr><td>%s</td><td><input type=\"radio\" name=\"REPLACE\" value=\"%s\" /></td><td>%s</td></tr>\n",
00533                      replacelab,
00534                      isps->suggested_word,
00535                      isps->suggested_word);
00536               replacelab=" ";
00537        }
00538        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",
00539               replacelab);
00540        printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#insert\" /></td><td>%s</td></tr>\n",
00541               insertlab);
00542 
00543        printf("<tr><td> </td><td><input type=\"checkbox\" name=\"REPLACEALL\" /></td><td>%s</td></tr>\n",
00544               replacealllab);
00545        printf("<tr><td> </td><td colspan=\"2\"><hr width=\"100%%\" /></td></tr>\n");
00546        printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#ignore\" /></td><td>%s</td></tr>\n",
00547               ignorelab);
00548        printf("<tr><td> </td><td><input type=\"radio\" name=\"REPLACE\" value=\"#ignoreall\" /></td><td>%s</td></tr>\n",
00549               ignorealllab);
00550        printf("</table>");
00551        printf("</td></tr></table><br />");
00552        printf("<table border=\"1\" cellpadding=\"8\" class=\"spellcheck-continue\"><tr><td>");
00553        printf("<input type=\"submit\" name=\"continue\" value=\"%s\" />\n",
00554                      continuelab);
00555        printf("<input type=\"submit\" name=\"finish\" value=\"%s\" />\n",
00556                      finishlab);
00557        printf("</td></tr></table>\n");
00558        printf("</td></tr></table>\n");
00559        printf("</td></tr></table>\n");
00560 }
00561 
00562 static FILE *opendict(const char *mode)
00563 {
00564 FILE   *fp;
00565 char   *p=malloc(sqwebmail_content_ispelldict ?
00566                      strlen(sqwebmail_content_ispelldict)+20:20);
00567 
00568        if (!p)       enomem();
00569        strcat(strcpy(p, sqwebmail_content_ispelldict ?
00570                      "sqwebmail-dict-":"sqwebmail-dict"),
00571               sqwebmail_content_ispelldict ? sqwebmail_content_ispelldict:"");
00572        fp=fopen(p, mode);
00573        free(p);
00574        return (fp);
00575 }
00576 
00577 static int spellignore(const char *word)
00578 {
00579 char   buf[100];
00580 const char *c;
00581 char   *p, *q;
00582 FILE   *fp=opendict("r");
00583 
00584        if (!fp)      return (0);
00585        while (fgets(buf, sizeof(buf), fp) != NULL)
00586        {
00587               if ((p=strchr(buf, '\n')) != 0)    *p=0;
00588               if (strcmp(word, buf) == 0)
00589               {
00590                      fclose(fp);
00591                      return (1);
00592               }
00593        }
00594        fclose(fp);
00595 
00596        c=cgi("globignore");
00597 
00598        p=malloc(strlen(c)+1);
00599        if (!p)       enomem();
00600        strcpy(p, c);
00601 
00602        for (q=p; (q=strtok(q, ":")) != 0; q=0)
00603               if (strcmp(q, word) == 0)
00604               {
00605                      free(p);
00606                      return (1);
00607               }
00608 
00609        return (0);
00610 }
00611 
00612 static void spelladd(const char *word)
00613 {
00614 FILE   *fp=opendict("a");
00615 
00616        if (fp)
00617        {
00618               fprintf(fp, "%s\n", word);
00619               fclose(fp);
00620        }
00621 }
00622 
00623 static char *spellreplace(const char *word)
00624 {
00625 char   *p, *q, *r;
00626 const char *c=cgi("globreplace");
00627 
00628        p=malloc(strlen(c)+1);
00629        if (!p)       enomem();
00630        strcpy(p, c);
00631        for (q=p; (q=strtok(q, ":")) != 0 && (r=strtok(0, ":")) != 0; q=0)
00632        {
00633               if (strcmp(q, word) == 0)
00634               {
00635                      q=malloc(strlen(r)+1);
00636                      if (!q)       enomem();
00637                      strcpy(q, r);
00638                      free(p);
00639                      return (q);
00640               }
00641        }
00642        free(p);
00643        return (0);
00644 }
00645 
00646 void spell_check_continue()
00647 {
00648 const char *filename=cgi("draftmessage");
00649 unsigned parnum=atol(cgi("row"));
00650 unsigned pos=atol(cgi("col"));
00651 char   *draftfilename;
00652 
00653        CHECKFILENAME(filename);
00654        draftfilename=maildir_find(INBOX "." DRAFTS, filename);
00655        if (!draftfilename)
00656        {
00657               output_form("folder.html");
00658               return;
00659        }
00660 
00661        if (search_spell(draftfilename, parnum, pos) &&
00662               *cgi("continue"))
00663               output_form("spellchk.html");
00664        else
00665        {
00666               cgi_put("draft", cgi("draftmessage"));
00667               cgi_put("previewmsg","SPELLCHK");
00668               output_form("newmsg.html");
00669        }
00670        free(draftfilename);
00671 }
00672 
00673 void ispell_cleanup()
00674 {
00675        if(ispellline) free(ispellline);
00676        ispellline=NULL;
00677 }