Back to index

webcit  8.12-dfsg
html2html.c
Go to the documentation of this file.
00001 /*
00002  * Output an HTML message, modifying it slightly to make sure it plays nice
00003  * with the rest of our web framework.
00004  *
00005  * Copyright (c) 2005-2012 by the citadel.org team
00006  *
00007  * This program is open source software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License, version 3.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  */
00015 
00016 #include "webcit.h"
00017 #include "webserver.h"
00018 
00019 
00020 /*
00021  * Strip surrounding single or double quotes from a string.
00022  */
00023 void stripquotes(char *s)
00024 {
00025        int len;
00026 
00027        if (!s) return;
00028 
00029        len = strlen(s);
00030        if (len < 2) return;
00031 
00032        if ( ( (s[0] == '\"') && (s[len-1] == '\"') ) || ( (s[0] == '\'') && (s[len-1] == '\'') ) ) {
00033               s[len-1] = 0;
00034               strcpy(s, &s[1]);
00035        }
00036 }
00037 
00038 
00039 /*
00040  * Check to see if a META tag has overridden the declared MIME character set.
00041  *
00042  * charset           Character set name (left unchanged if we don't do anything)
00043  * meta_http_equiv   Content of the "http-equiv" portion of the META tag
00044  * meta_content             Content of the "content" portion of the META tag
00045  */
00046 void extract_charset_from_meta(char *charset, char *meta_http_equiv, char *meta_content)
00047 {
00048        char *ptr;
00049        char buf[64];
00050 
00051        if (!charset) return;
00052        if (!meta_http_equiv) return;
00053        if (!meta_content) return;
00054 
00055 
00056        if (strcasecmp(meta_http_equiv, "Content-type")) return;
00057 
00058        ptr = strchr(meta_content, ';');
00059        if (!ptr) return;
00060 
00061        safestrncpy(buf, ++ptr, sizeof buf);
00062        striplt(buf);
00063        if (!strncasecmp(buf, "charset=", 8)) {
00064               strcpy(charset, &buf[8]);
00065 
00066               /*
00067                * The brain-damaged webmail program in Microsoft Exchange declares
00068                * a charset of "unicode" when they really mean "UTF-8".  GNU iconv
00069                * treats "unicode" as an alias for "UTF-16" so we have to manually
00070                * fix this here, otherwise messages generated in Exchange webmail
00071                * show up as a big pile of weird characters.
00072                */
00073               if (!strcasecmp(charset, "unicode")) {
00074                      strcpy(charset, "UTF-8");
00075               }
00076 
00077               /* Remove wandering punctuation */
00078               if ((ptr=strchr(charset, '\"'))) *ptr = 0;
00079               striplt(charset);
00080        }
00081 }
00082 
00083 
00084 
00085 /*
00086  * Sanitize and enhance an HTML message for display.
00087  * Also convert weird character sets to UTF-8 if necessary.
00088  * Also fixup img src="cid:..." type inline images to fetch the image
00089  *
00090  */
00091 void output_html(const char *supplied_charset, int treat_as_wiki, int msgnum, StrBuf *Source, StrBuf *Target) {
00092        char buf[SIZ];
00093        char *msg;
00094        char *ptr;
00095        char *msgstart;
00096        char *msgend;
00097        StrBuf *converted_msg;
00098        int buffer_length = 1;
00099        int line_length = 0;
00100        int content_length = 0;
00101        char new_window[SIZ];
00102        int brak = 0;
00103        int alevel = 0;
00104        int scriptlevel = 0;
00105        int script_start_pos = (-1);
00106        int i;
00107        int linklen;
00108        char charset[128];
00109        StrBuf *BodyArea = NULL;
00110 #ifdef HAVE_ICONV
00111        iconv_t ic = (iconv_t)(-1) ;
00112        char *ibuf;                   /* Buffer of characters to be converted */
00113        char *obuf;                   /* Buffer for converted characters      */
00114        size_t ibuflen;               /* Length of input buffer               */
00115        size_t obuflen;               /* Length of output buffer              */
00116        char *osav;                   /* Saved pointer to output buffer       */
00117 #endif
00118        if (Target == NULL)
00119               Target = WC->WBuf;
00120 
00121        safestrncpy(charset, supplied_charset, sizeof charset);
00122        msg = strdup("");
00123        sprintf(new_window, "<a target=\"%s\" href=", TARGET);
00124 
00125        if (Source == NULL) while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00126               line_length = strlen(buf);
00127               buffer_length = content_length + line_length + 2;
00128               ptr = realloc(msg, buffer_length);
00129               if (ptr == NULL) {
00130                      StrBufAppendPrintf(Target, "<b>");
00131                      StrBufAppendPrintf(Target, _("realloc() error! couldn't get %d bytes: %s"),
00132                                    buffer_length + 1,
00133                                    strerror(errno));
00134                      StrBufAppendPrintf(Target, "</b><br><br>\n");
00135                      while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00137                      }
00138                      free(msg);
00139                      return;
00140               }
00141               msg = ptr;
00142               strcpy(&msg[content_length], buf);
00143               content_length += line_length;
00144               strcpy(&msg[content_length], "\n");
00145               content_length += 1;
00146        }
00147        else {
00148               content_length = StrLength(Source);
00149               free(msg);
00150               msg = (char*) ChrPtr(Source);/* TODO: remove cast */
00151               buffer_length = content_length;
00152        }
00153 
00155        ptr = msg + 1;
00156        msgstart = msg;
00157        msgend = &msg[content_length];
00158 
00159        while (ptr < msgend) {
00160 
00162               ptr = strchr(ptr, '<');
00163               if ((ptr == NULL) || (ptr >= msgend)) break;
00164               ++ptr;
00165               if ((ptr == NULL) || (ptr >= msgend)) break;
00166 
00167               /*
00168                *  Look for META tags.  Some messages (particularly in
00169                *  Asian locales) illegally declare a message's character
00170                *  set in the HTML instead of in the MIME headers.  This
00171                *  is wrong but we have to work around it anyway.
00172                */
00173               if (!strncasecmp(ptr, "META", 4)) {
00174 
00175                      char *meta_start;
00176                      char *meta_end;
00177                      int meta_length;
00178                      char *meta;
00179                      char *meta_http_equiv;
00180                      char *meta_content;
00181                      char *spaceptr;
00182 
00183                      meta_start = &ptr[4];
00184                      meta_end = strchr(ptr, '>');
00185                      if ((meta_end != NULL) && (meta_end <= msgend)) {
00186                             meta_length = meta_end - meta_start + 1;
00187                             meta = malloc(meta_length + 1);
00188                             safestrncpy(meta, meta_start, meta_length);
00189                             meta[meta_length] = 0;
00190                             striplt(meta);
00191                             if (!strncasecmp(meta, "HTTP-EQUIV=", 11)) {
00192                                    meta_http_equiv = strdup(&meta[11]);
00193                                    spaceptr = strchr(meta_http_equiv, ' ');
00194                                    if (spaceptr != NULL) {
00195                                           *spaceptr = 0;
00196                                           meta_content = strdup(++spaceptr);
00197                                           if (!strncasecmp(meta_content, "content=", 8)) {
00198                                                  strcpy(meta_content, &meta_content[8]);
00199                                                  stripquotes(meta_http_equiv);
00200                                                  stripquotes(meta_content);
00201                                                  extract_charset_from_meta(charset,
00202                                                                meta_http_equiv, meta_content);
00203                                           }
00204                                           free(meta_content);
00205                                    }
00206                                    free(meta_http_equiv);
00207                             }
00208                             free(meta);
00209                      }
00210               }
00211 
00212               /*
00213                * Any of these tags cause everything up to and including
00214                * the tag to be removed.
00215                */    
00216               if ( (!strncasecmp(ptr, "HTML", 4))
00217                             ||(!strncasecmp(ptr, "HEAD", 4))
00218                             ||(!strncasecmp(ptr, "/HEAD", 5))
00219                             ||(!strncasecmp(ptr, "BODY", 4)) ) {
00220                      char *pBody = NULL;
00221 
00222                      if (!strncasecmp(ptr, "BODY", 4)) {
00223                             pBody = ptr;
00224                      }
00225                      ptr = strchr(ptr, '>');
00226                      if ((ptr == NULL) || (ptr >= msgend)) break;
00227                      if ((pBody != NULL) && (ptr - pBody > 4)) {
00228                             char* src;
00229                             char *cid_start, *cid_end;
00230 
00231                             *ptr = '\0';
00232                             pBody += 4; 
00233                             while ((isspace(*pBody)) && (pBody < ptr))
00234                                    pBody ++;
00235                             BodyArea = NewStrBufPlain(NULL,  ptr - pBody);
00236 
00237                             if (pBody < ptr) {
00238                                    src = strstr(pBody, "cid:");
00239                                    if (src) {
00240                                           cid_start = src + 4;
00241                                           cid_end = cid_start;
00242                                           while ((*cid_end != '"') && 
00243                                                         !isspace(*cid_end) &&
00244                                                         (cid_end < ptr))
00245                                                  cid_end ++;
00246 
00247                                           /* copy tag and attributes up to src="cid: */
00248                                           StrBufAppendBufPlain(BodyArea, pBody, src - pBody, 0);
00249 
00250                                           /* add in /webcit/mimepart/<msgno>/CID/ 
00251                                              trailing / stops dumb URL filters getting excited */
00252                                           StrBufAppendPrintf(BodyArea,
00253                                                         "/webcit/mimepart/%d/",msgnum);
00254                                           StrBufAppendBufPlain(BodyArea, cid_start, cid_end - cid_start, 0);
00255 
00256                                           if (ptr - cid_end > 0)
00257                                                  StrBufAppendBufPlain(BodyArea, 
00258                                                                cid_end + 1, 
00259                                                                ptr - cid_end, 0);
00260                                    }
00261                                    else 
00262                                           StrBufAppendBufPlain(BodyArea, pBody, ptr - pBody, 0);
00263                             }
00264                             *ptr = '>';
00265                      }
00266                      ++ptr;
00267                      if ((ptr == NULL) || (ptr >= msgend)) break;
00268                      msgstart = ptr;
00269               }
00270 
00271               /*
00272                * Any of these tags cause everything including and following
00273                * the tag to be removed.
00274                */
00275               if ( (!strncasecmp(ptr, "/HTML", 5))
00276                             ||(!strncasecmp(ptr, "/BODY", 5)) ) {
00277                      --ptr;
00278                      msgend = ptr;
00279                      strcpy(ptr, "");
00280 
00281               }
00282 
00283               ++ptr;
00284        }
00285        if (msgstart > msg) {
00286               strcpy(msg, msgstart);
00287        }
00288 
00289        /* Now go through the message, parsing tags as necessary. */
00290        converted_msg = NewStrBufPlain(NULL, content_length + 8192);
00291 
00292 
00294 #ifdef HAVE_ICONV
00295        if ( (strcasecmp(charset, "us-ascii"))
00296                      && (strcasecmp(charset, "UTF-8"))
00297                      && (strcasecmp(charset, ""))
00298           ) {
00299               syslog(9, "Converting %s to UTF-8\n", charset);
00300               ctdl_iconv_open("UTF-8", charset, &ic);
00301               if (ic == (iconv_t)(-1) ) {
00302                      syslog(5, "%s:%d iconv_open() failed: %s\n",
00303                                    __FILE__, __LINE__, strerror(errno));
00304               }
00305        }
00306        if  (Source == NULL) {
00307               if (ic != (iconv_t)(-1) ) {
00308                      ibuf = msg;
00309                      ibuflen = content_length;
00310                      obuflen = content_length + (content_length / 2) ;
00311                      obuf = (char *) malloc(obuflen);
00312                      osav = obuf;
00313                      iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
00314                      content_length = content_length + (content_length / 2) - obuflen;
00315                      osav[content_length] = 0;
00316                      free(msg);
00317                      msg = osav;
00318                      iconv_close(ic);
00319               }
00320        }
00321        else {
00322               if (ic != (iconv_t)(-1) ) {
00323                      StrBuf *Buf = NewStrBufPlain(NULL, StrLength(Source) + 8096);;
00324                      StrBufConvert(Source, Buf, &ic);
00325                      FreeStrBuf(&Buf);
00326                      iconv_close(ic);
00327                      msg = (char*)ChrPtr(Source); /* TODO: get rid of this. */
00328               }
00329        }
00330 
00331 #endif
00332 
00333        /*
00334         *     At this point, the message has been stripped down to
00335         *     only the content inside the <BODY></BODY> tags, and has
00336         *     been converted to UTF-8 if it was originally in a foreign
00337         *     character set.  The text is also guaranteed to be null
00338         *     terminated now.
00339         */
00340 
00341        if (converted_msg == NULL) {
00342               StrBufAppendPrintf(Target, "Error %d: %s<br>%s:%d", errno, strerror(errno), __FILE__, __LINE__);
00343               goto BAIL;
00344        }
00345 
00346        if (BodyArea != NULL) {
00347               StrBufAppendBufPlain(converted_msg, HKEY("<table "), 0);  
00348               StrBufAppendBuf(converted_msg, BodyArea, 0);
00349               StrBufAppendBufPlain(converted_msg, HKEY(" width=\"100%\"><tr><td>"), 0);
00350        }
00351        ptr = msg;
00352        msgend = strchr(msg, 0);
00353        while (ptr < msgend) {
00354 
00356               if (!strncasecmp(ptr, "<script", 7)) {
00357                      if (scriptlevel == 0) {
00358                             script_start_pos = StrLength(converted_msg);
00359                      }
00360                      ++scriptlevel;
00361               }
00362               if (!strncasecmp(ptr, "</script", 8)) {
00363                      --scriptlevel;
00364               }
00365 
00372               if (!strncasecmp(ptr, "<a href=\"mailto:", 16)) {
00373                      content_length += 64;
00374                      StrBufAppendPrintf(converted_msg,
00375                                    "<a href=\"display_enter?force_room=_MAIL_?recp=");
00376                      ptr = &ptr[16];
00377                      ++alevel;
00378                      ++brak;
00379               }
00381               else if (!strncasecmp(ptr, "<a href=\"", 9)) {
00382                      ++alevel;
00383                      ++brak;
00384                      if ( ((strchr(ptr, ':') < strchr(ptr, '/')))
00385                                    &&  ((strchr(ptr, '/') < strchr(ptr, '>'))) 
00386                         ) {
00387                             /* open external links to new window */
00388                             StrBufAppendPrintf(converted_msg, new_window);
00389                             ptr = &ptr[8];
00390                      }
00391                      else if (
00392                             (treat_as_wiki)
00393                             && (strncasecmp(ptr, "<a href=\"wiki?", 14))
00394                             && (strncasecmp(ptr, "<a href=\"dotgoto?", 17))
00395                             && (strncasecmp(ptr, "<a href=\"knrooms?", 17))
00396                      ) {
00397                             content_length += 64;
00398                             StrBufAppendPrintf(converted_msg, "<a href=\"wiki?go=");
00399                             StrBufUrlescAppend(converted_msg, WC->CurRoom.name, NULL);
00400                             StrBufAppendPrintf(converted_msg, "?page=");
00401                             ptr = &ptr[9];
00402                      }
00403                      else {
00404                             StrBufAppendPrintf(converted_msg, "<a href=\"");
00405                             ptr = &ptr[9];
00406                      }
00407               }
00409               else if (!strncasecmp(ptr, "<img ", 5)) {
00410                      char *cid_start, *cid_end;
00411                      char* tag_end=strchr(ptr,'>');
00412                      char* src;
00413                      /* FIXME - handle this situation (maybe someone opened an <img cid... 
00414                       * and then ended the message)
00415                       */
00416                      if (!tag_end) {
00417                             syslog(9, "tag_end is null and ptr is:\n");
00418                             syslog(9, "%s\n", ptr);
00419                             syslog(9, "Theoretical bytes remaining: %d\n", (int)(msgend - ptr));
00420                      }
00421 
00422                      src=strstr(ptr, "src=\"cid:");
00423                      ++brak;
00424 
00425                      if (src
00426                          && isspace(*(src-1))
00427                             && tag_end
00428                             && (cid_start=strchr(src,':'))
00429                             && (cid_end=strchr(cid_start,'"'))
00430                             && (cid_end < tag_end)
00431                      ) {
00432                             /* copy tag and attributes up to src="cid: */
00433                             StrBufAppendBufPlain(converted_msg, ptr, src - ptr, 0);
00434                             cid_start++;
00435 
00436                             /* add in /webcit/mimepart/<msgno>/CID/ 
00437                                trailing / stops dumb URL filters getting excited */
00438                             StrBufAppendPrintf(converted_msg,
00439                                           " src=\"/webcit/mimepart/%d/",msgnum);
00440                             StrBufAppendBufPlain(converted_msg, cid_start, cid_end - cid_start, 0);
00441                             StrBufAppendBufPlain(converted_msg, "/\"", -1, 0);
00442 
00443                             ptr = cid_end+1;
00444                      }
00445                      StrBufAppendBufPlain(converted_msg, ptr, tag_end - ptr, 0);
00446                      ptr = tag_end;
00447               }
00448 
00453               else if ( (brak == 0) && (alevel == 0)
00454                    && (!strncasecmp(ptr, "http://", 7))) {
00456                             int strlenptr;
00457                             linklen = 0;
00458                             
00459                             strlenptr = strlen(ptr);
00460                             for (i=0; i<=strlenptr; ++i) {
00461                                    if ((ptr[i]==0)
00462                                       ||(isspace(ptr[i]))
00463                                       ||(ptr[i]==10)
00464                                       ||(ptr[i]==13)
00465                                       ||(ptr[i]=='(')
00466                                       ||(ptr[i]==')')
00467                                       ||(ptr[i]=='<')
00468                                       ||(ptr[i]=='>')
00469                                       ||(ptr[i]=='[')
00470                                       ||(ptr[i]==']')
00471                                       ||(ptr[i]=='"')
00472                                       ||(ptr[i]=='\'')
00473                                    ) linklen = i;
00474                                    /* did s.b. send us an entity? */
00475                                    if (ptr[i] == '&') {
00476                                           if ((ptr[i+2] ==';') ||
00477                                               (ptr[i+3] ==';') ||
00478                                               (ptr[i+5] ==';') ||
00479                                               (ptr[i+6] ==';') ||
00480                                               (ptr[i+7] ==';'))
00481                                                  linklen = i;
00482                                    }
00483                                    if (linklen > 0) break;
00484                             }
00485                             if (linklen > 0) {
00486                                    char *ltreviewptr;
00487                                    char *nbspreviewptr;
00488                                    char linkedchar;
00489                                    int len;
00490                                    
00491                                    len = linklen;
00492                                    linkedchar = ptr[len];
00493                                    ptr[len] = '\0';
00494                                    /* spot for some subject strings tinymce tends to give us. */
00495                                    ltreviewptr = strchr(ptr, '<');
00496                                    if (ltreviewptr != NULL) {
00497                                           *ltreviewptr = '\0';
00498                                           linklen = ltreviewptr - ptr;
00499                                    }
00500 
00501                                    nbspreviewptr = strstr(ptr, "&nbsp;");
00502                                    if (nbspreviewptr != NULL) {
00503                                           /* nbspreviewptr = '\0'; */
00504                                           linklen = nbspreviewptr - ptr;
00505                                    }
00506                                    if (ltreviewptr != 0)
00507                                           *ltreviewptr = '<';
00508 
00509                                    ptr[len] = linkedchar;
00510 
00511                                    content_length += (32 + linklen);
00512                                         StrBufAppendPrintf(converted_msg, "%s\"", new_window);
00513                                    StrBufAppendBufPlain(converted_msg, ptr, linklen, 0);
00514                                    StrBufAppendPrintf(converted_msg, "\">");
00515                                    StrBufAppendBufPlain(converted_msg, ptr, linklen, 0);
00516                                    ptr += linklen;
00517                                    StrBufAppendPrintf(converted_msg, "</A>");
00518                             }
00519               }
00520               else {
00521                      StrBufAppendBufPlain(converted_msg, ptr, 1, 0);
00522                      ptr++;
00523               }
00524 
00525 
00526               if ((ptr >= msg) && (ptr <= msgend)) {
00527                      /*
00528                       * We need to know when we're inside a tag,
00529                       * so we don't turn things that look like URL's into
00530                       * links, when they're already links - or image sources.
00531                       */
00532                      if ((ptr > msg) && (*(ptr-1) == '<')) {
00533                             ++brak;
00534                      }
00535                      if ((ptr > msg) && (*(ptr-1) == '>')) {
00536                             --brak;
00537                             if ((scriptlevel == 0) && (script_start_pos >= 0)) {
00538                                    StrBufCutRight(converted_msg, StrLength(converted_msg) - script_start_pos);
00539                                    script_start_pos = (-1);
00540                             }
00541                      }
00542                      if (!strncasecmp(ptr, "</A>", 3)) --alevel;
00543               }
00544        }
00545 
00546        if (BodyArea != NULL) {
00547               StrBufAppendBufPlain(converted_msg, HKEY("</td></tr></table>"), 0);  
00548               FreeStrBuf(&BodyArea);
00549        }
00550 
00556        StrBufAppendBuf(Target, converted_msg, 0);
00557 
00558 BAIL:  
00559        StrBufAppendPrintf(Target, "<br><br>\n");
00560 
00562        FreeStrBuf(&converted_msg);
00563        if ((msg != NULL) && (Source == NULL)) free(msg);
00564 }
00565 
00566 
00567 
00568 
00569 
00570 
00571 /*
00572  * Look for URL's embedded in a buffer and make them linkable.  We use a
00573  * target window in order to keep the Citadel session in its own window.
00574  */
00575 void UrlizeText(StrBuf* Target, StrBuf *Source, StrBuf *WrkBuf)
00576 {
00577        int len, UrlLen, Offset, TrailerLen;
00578        const char *start, *end, *pos;
00579        
00580        FlushStrBuf(Target);
00581 
00582        start = NULL;
00583        len = StrLength(Source);
00584        end = ChrPtr(Source) + len;
00585        for (pos = ChrPtr(Source); (pos < end) && (start == NULL); ++pos) {
00586               if (!strncasecmp(pos, "http://", 7))
00587                      start = pos;
00588               else if (!strncasecmp(pos, "ftp://", 6))
00589                      start = pos;
00590        }
00591 
00592        if (start == NULL) {
00593               StrBufAppendBuf(Target, Source, 0);
00594               return;
00595        }
00596        FlushStrBuf(WrkBuf);
00597 
00598        for (pos = ChrPtr(Source) + len; pos > start; --pos) {
00599               if (  (!isprint(*pos))
00600                  || (isspace(*pos))
00601                  || (*pos == '{')
00602                  || (*pos == '}')
00603                  || (*pos == '|')
00604                  || (*pos == '\\')
00605                  || (*pos == '^')
00606                  || (*pos == '[')
00607                  || (*pos == ']')
00608                  || (*pos == '`')
00609                  || (*pos == '<')
00610                  || (*pos == '>')
00611                  || (*pos == '(')
00612                  || (*pos == ')')
00613               ) {
00614                      end = pos;
00615               }
00616        }
00617        
00618        UrlLen = end - start;
00619        StrBufAppendBufPlain(WrkBuf, start, UrlLen, 0);
00620 
00621        Offset = start - ChrPtr(Source);
00622        if (Offset != 0)
00623               StrBufAppendBufPlain(Target, ChrPtr(Source), Offset, 0);
00624        StrBufAppendPrintf(Target, "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
00625                         LB, QU, ChrPtr(WrkBuf), QU, QU, TARGET, 
00626                         QU, RB, ChrPtr(WrkBuf), LB, RB);
00627 
00628        TrailerLen = StrLength(Source) - (end - ChrPtr(Source));
00629        if (TrailerLen > 0)
00630               StrBufAppendBufPlain(Target, end, TrailerLen, 0);
00631 }
00632 
00633 
00634 void url(char *buf, size_t bufsize)
00635 {
00636        int len, UrlLen, Offset, TrailerLen, outpos;
00637        char *start, *end, *pos;
00638        char urlbuf[SIZ];
00639        char outbuf[SIZ];
00640 
00641        start = NULL;
00642        len = strlen(buf);
00643        if (len > bufsize) {
00644               syslog(1, "URL: content longer than buffer!");
00645               return;
00646        }
00647        end = buf + len;
00648        for (pos = buf; (pos < end) && (start == NULL); ++pos) {
00649               if (!strncasecmp(pos, "http://", 7))
00650                      start = pos;
00651               if (!strncasecmp(pos, "ftp://", 6))
00652                      start = pos;
00653        }
00654 
00655        if (start == NULL)
00656               return;
00657 
00658        for (pos = buf+len; pos > start; --pos) {
00659               if (  (!isprint(*pos))
00660                  || (isspace(*pos))
00661                  || (*pos == '{')
00662                  || (*pos == '}')
00663                  || (*pos == '|')
00664                  || (*pos == '\\')
00665                  || (*pos == '^')
00666                  || (*pos == '[')
00667                  || (*pos == ']')
00668                  || (*pos == '`')
00669                  || (*pos == '<')
00670                  || (*pos == '>')
00671                  || (*pos == '(')
00672                  || (*pos == ')')
00673               ) {
00674                      end = pos;
00675               }
00676        }
00677        
00678        UrlLen = end - start;
00679        if (UrlLen > sizeof(urlbuf)){
00680               syslog(1, "URL: content longer than buffer!");
00681               return;
00682        }
00683        memcpy(urlbuf, start, UrlLen);
00684        urlbuf[UrlLen] = '\0';
00685 
00686        Offset = start - buf;
00687        if ((Offset != 0) && (Offset < sizeof(outbuf)))
00688               memcpy(outbuf, buf, Offset);
00689        outpos = snprintf(&outbuf[Offset], sizeof(outbuf) - Offset,  
00690                        "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
00691                        LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
00692        if (outpos >= sizeof(outbuf) - Offset) {
00693               syslog(1, "URL: content longer than buffer!");
00694               return;
00695        }
00696 
00697        TrailerLen = len - (end - start);
00698        if (TrailerLen > 0)
00699               memcpy(outbuf + Offset + outpos, end, TrailerLen);
00700        if (Offset + outpos + TrailerLen > bufsize) {
00701               syslog(1, "URL: content longer than buffer!");
00702               return;
00703        }
00704        memcpy (buf, outbuf, Offset + outpos + TrailerLen);
00705        *(buf + Offset + outpos + TrailerLen) = '\0';
00706 }
00707