#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
              


#include "../global.h"

#include "shared_cs_util.h"
#include "shared_msg_line.h"
#include "shared_translate_url.h"
#include "shared_strtrm.h"
#include "shared_system_data.h"
    
LINE_NODE *line_head = 0;    

#define MAX_SMILE_CODE 12

const char *code[MAX_SMILE_CODE] =
   {":-)", ":lol:", ":-o", ":!:", ":?:", ":idea:", ":-(", ":shock:", ":-x", "8-)", ";-(", ":-?" };
const char *icon_img[MAX_SMILE_CODE] =
  {"icon_smile.gif", "icon_lol.gif", "icon_surprised.gif", "icon_exclaim.gif", "icon_question.gif", "icon_idea.gif",
   "icon_sad.gif", "icon_eek.gif", "icon_mad.gif", "icon_cool.gif", "icon_wink.gif", "icon_confused.gif"};

#define MAX_BB_CODE 7 

#define BB_BOLD "**" 
#define BB_UNDER_LINE "__" 
#define BB_ITALIC "||" 
#define BB_HEADING_4 "!!" 
#define BB_HEADING_3 "!!!" 
#define BB_HEADING_2 "!!!!" 
#define BB_PRE "%%" 

const char *bb_code_array[MAX_BB_CODE] ={ BB_BOLD, BB_UNDER_LINE, BB_ITALIC, BB_HEADING_2, BB_HEADING_3, BB_HEADING_4, BB_PRE };
const char *start_tag_array[MAX_BB_CODE] ={ "<b>", "<u>", "<i>", "<span class=\"bb_h2\">", "<span class=\"bb_h3\">", "<span class=\"bb_h4\">", "<pre>"};
const char *end_tag_array[MAX_BB_CODE] ={"</b>", "</u>", "</i>", "</span>", "</span>", "</span>", "</pre>"};



#define MINIMUM_SIZE 256


typedef struct mystring
  {
   int buf_len;
   int used_len;
   char *buf;
  }MYSTRING;
  
  
void
mystring_init(MYSTRING *the_string, int len)
{
 the_string->buf = (char *) malloc(len * sizeof(char));
 if(!the_string->buf)
     cs_critical_error(ERR_MALLOC_FAILED, "mystring_init()");
 *the_string->buf = '\0';    
 the_string->buf_len = len;
 the_string->used_len = 0;
}


void mystring_free(MYSTRING *the_string)
{
 if(the_string->buf)
  {
     free(the_string->buf);
     the_string->buf_len = 0;
     the_string->used_len = 0;
  }
}

     


void mystring_append(MYSTRING *the_string, const char *appended)
{


 int append_len;
 int bytes_to_add;
 
 append_len = strlen(appended);
 if( (append_len + the_string->used_len + 1) > the_string->buf_len)
  {
   bytes_to_add = (append_len * 2) < MINIMUM_SIZE ? MINIMUM_SIZE : (append_len * 2);
   the_string->buf = realloc(the_string->buf, the_string->buf_len + bytes_to_add);
   if(!the_string->buf)
       cs_critical_error(ERR_MALLOC_FAILED, "mystring_append()");
   the_string->buf_len += bytes_to_add;    
  }
  
 strncat(the_string->buf, appended, append_len);
 the_string->used_len = strlen(the_string->buf);
}

 
  
       
void
mystring_append_char(MYSTRING *the_string, char ch)
{
 char charstring[2];
 
 *charstring = ch;
 *(charstring + 1) = '\0';
 
 mystring_append(the_string, charstring);
}        
  



static
char *shrink_copy(char *string)
{
 char *result;
 int len;
 
 len = strlen(string) + 1;
 
 result = (char *) malloc(len * sizeof(char));
 if(!result)
    cs_critical_error(ERR_MALLOC_FAILED, "shrink_copy()");
 strncpy(result, string,len);
 return result;
}



/* a url path is at least 5 chars long and has at least one period */
static int
is_url_path (char *string, int *count)
{
  char *ptr;
  int dots;


  for (*count = 0, dots = 0, ptr = string; *ptr && !isspace (*ptr);
       ptr++, (*count)++)
    if (*ptr == '.')
      dots++;

  if ((dots < 1) || (*count < 5))
    return 0;

  /* exclude trailing punctuation */
  for (ptr--; (*count >= 5) && ispunct (*ptr); ptr--, (*count)--)
    if (*ptr == '.')
      dots--;

  return (dots >= 1) && (*count >= 5);

}


/* a mail address is at least 5 chars long and has just one @ and at least one periods */
static int
is_mail_address (char *string, int *count)
{
  char *ptr;
  int dots;
  int ats;


  for (*count = 0, dots = 0, ats = 0, ptr = string; *ptr && !isspace (*ptr);
       ptr++, (*count)++)
    {
      if (*ptr == '.')
	dots++;
      else if (*ptr == '@')
	ats++;
    }


  if ((dots < 1) || (*count < 5) || (ats != 1))
    return 0;

  /* exclude trailing punctuation */
  for (ptr--; (*count >= 5) && ispunct (*ptr); ptr--, (*count)--)
    {
      if (*ptr == '.')
	dots--;
      else if (*ptr == '@')
	ats--;
    }

  return (dots >= 1) && (*count >= 5) && (ats == 1);

}

static void
append_anchor (const char *protocol, char *string, int count, MYSTRING *the_string)
{
  char *ptr;
  int i;

  mystring_append (the_string, "<a href=\"");
  mystring_append (the_string, protocol);
  for (ptr = string, i = 1; *ptr && (i <= count); ptr++, i++)
    mystring_append_char(the_string, *ptr);

  if(!strcmp(protocol, "http://") || !strcmp(protocol, "https://") || !strcmp(protocol, "ftp://"))
   {
     mystring_append(the_string, "\" onclick=\"newwin=window.open('");
     mystring_append(the_string, protocol);
     for (ptr = string, i = 1; *ptr && (i <= count); ptr++, i++)
        mystring_append_char(the_string, *ptr);
     mystring_append(the_string, "','link','menubar=yes,toolbar=yes,status=yes,scrollbars=yes,resizable=yes,width=760,height=500,location=yes'); newwin.focus(); return false\" >");
   }
  else
    mystring_append(the_string, "\">");    
}




int 
is_blank_line(char *line)
{
 char *ptr;
 
 for(ptr=line; *ptr; ptr++)
   if(!isspace(*ptr))
     return 0;
     
 return 1;
}
 
     

void
alloc_formatted_line (char *line, int do_anchors, int insert_br, char **new_line)
{
  char *ptr, *ptr2 =0, *ptr3 =0;
  char temp[20];
  int do_slashes = 0;		/* possibility of ftp://, telnet:// or http:// urls */
  int do_mail = 0;
  int count;
  int i, j, start_bb_count =0, end_bb_count =0;
  int last_was_space = 0;
  char examined_char;
  MYSTRING result;
  int is_https;
  char icon_path[MAX_PATH +1];
  char icon_end_path[MAX_PATH +1];

  
  snprintf(icon_path, MAX_PATH +1, "<img src=\"%s%s%d/icon_", system_data_str("IMAGE_PATH"), THEME_DIR_PREFIX, global_theme); 
  snprintf(icon_end_path, MAX_PATH +1, ".gif\" alt=\"\"/>"); 
 
  mystring_init(&result, MINIMUM_SIZE);
 
  /* do some checks to see if we need to look for URL's * */
  if (do_anchors)
    {
      do_anchors = (int) strchr (line, ':');	/* must have at least a ':' */
      do_slashes = do_anchors && (int) strstr (line, "://");
      do_mail = do_anchors && (int) strchr (line, '@');
    }

  if(insert_br && is_blank_line(line))
    mystring_append(&result, "<br />");
  else
   {  
     
  for (ptr = line; *ptr;)
    {
      examined_char = *ptr;
      start_bb_count = 0;
      end_bb_count =0;
      switch (*ptr)
	{
	case '<':
	  for(j =0; j<MAX_BB_CODE && !start_bb_count; j++)
	  {
	    ptr2 = strstr(ptr, start_tag_array[j]);
	    if(ptr2 && ptr2 == ptr)
	       start_bb_count++;
	  }
	  if(start_bb_count)
	  {
              mystring_append (&result, start_tag_array[j-1]);
	      ptr = ptr + strlen(start_tag_array[j-1]);
	  }
	  if(!start_bb_count)
	  {
               for(j =0; j< MAX_BB_CODE && !end_bb_count; j++)
	       {
		    ptr2 = strstr(ptr, end_tag_array[j]);
		    if(ptr2 && ptr2 == ptr)
			  end_bb_count++;
	       }
	       if(end_bb_count)
	       {
                  mystring_append (&result, end_tag_array[j-1]);
	          ptr = ptr + strlen(end_tag_array[j-1]);
	       }
	  }
	  if( !start_bb_count && !end_bb_count)
	  {
            ptr2 = strstr(ptr, icon_path);
            if(ptr2)
	    {
              ptr2 = ptr2 + strlen(icon_path);
              ptr3 = strstr(ptr2, icon_end_path);
	    }

            if(ptr2 && ptr3)
	    {
              mystring_append (&result, icon_path);
              *temp = '\0';
	      strncat(temp, ptr2, (ptr3 - ptr2));
              mystring_append (&result, temp);
              mystring_append (&result, icon_end_path);

	      ptr = ptr3 + strlen(icon_end_path);	  
	      
           }
	   else if (*(ptr + 1) && *(ptr + 2))	/* if there are at least 2 more chars */
	    {
	      ptr2 = strchr ("bBiIuU/", *(ptr + 1));	/*look at next char */
	      if (ptr2)		/* next char one of biu */
		if (*ptr2 == '/')
		  {
		    if (*(ptr + 3) == '>')	/* looks like "</X> */
		      {
			ptr2 = strchr ("bBiIuU", *(ptr + 2));
			if (ptr2)	/* char after slash is biu */
			  {
			    snprintf (temp, 20, "</%c>", *ptr2);
			    mystring_append(&result, temp);
			    ptr += 4;
			  }
			else	/* did not meet </X> tests */
			  {
			    mystring_append (&result, "&lt;");
			    ptr++;
			  }
		      }
		    else	/* doesn't look like </X> */
		      {
			mystring_append(&result,"&lt;");
			ptr++;
		      }
		  }
		else /* second char is one of 'bBuUIi' */ if (*(ptr + 2) == '>')	/* then it's <b> or <i> etc */
		  {
		    snprintf (temp, 20, "<%c>", *(ptr + 1));
		    mystring_append (&result,temp);
		    ptr += 3;
		  }
		else
		  {
		    mystring_append(&result, "&lt;");
		    ptr++;
		  }
	      else		/* not < followed by biu/ */
		{
		  mystring_append(&result, "&lt;");
		  ptr++;
		}
	    }
	  else			/* not at least two more characters after < */
	    {
	      mystring_append(&result, "&lt;");
	      ptr++;
	    }
	  }
	  break;


	case '>':
	  mystring_append(&result,"&gt;");
	  ptr++;
	  break; 

	case '&':
	  mystring_append(&result, "&amp;");
	  ptr++;
	  break;

	case 'h':
	case 'H':
	  if (!do_slashes || !(ptr == line || (*(ptr - 1) == ' ') || (*(ptr - 1) == '>')))
	    {
	      mystring_append_char(&result, *ptr);
	      ptr++;
	    }
	  else			/* we know there's a space before the 'h' */
	    {
	      *temp = '\0';
	      strncat (temp, ptr, 12);	/* 12 = strlen("http://a.b.c") */
	      if (strlen (temp) != 12)
		{
		  mystring_append_char(&result, *ptr);
		  ptr++;
		}
	      else
		{
		  for (ptr2 = temp; *ptr2; ptr2++)
		    *ptr2 = tolower (*ptr2);
		  is_https = !strncmp(temp, "https://", 8);
		  if (!is_https && strncmp (temp, "http://", 7) ) /* i.e. it's not http:// and it's not https:// */
		    {
		      mystring_append_char(&result, *ptr);
		      ptr++;
		    }
		  else
		    {
		      if (is_url_path (is_https?ptr + 8:ptr + 7, &count))
			{
			  append_anchor (is_https?"https://":"http://", is_https?ptr + 8:ptr + 7, count, &result);
			  for (i = 1; i <= ((is_https?8:7) + count); i++)
			    {
			      mystring_append_char(&result, *ptr);
			      ptr++;
			    }
			  mystring_append (&result, "</a>");
			}
		      else
			{
			  mystring_append_char(&result, *ptr);
			  ptr++;
			}
		    }
		}
	    }
	  break;


	case 'f':
	case 'F':
	  if (!do_slashes || !(ptr == line || (*(ptr - 1) == ' ')))
	    {
	      mystring_append_char (&result, *ptr);
	      ptr++;
	    }
	  else			/* we know there's a space before the 'f' */
	    {
	      *temp = '\0';
	      strncat (temp, ptr, 11);	/* 11 = strlen("ftp://a.b.c") */
	      if (strlen (temp) != 11)
		{
		  mystring_append_char  (&result, *ptr);
		  ptr++;
		}
	      else
		{
		  for (ptr2 = temp; *ptr2; ptr2++)
		    *ptr2 = tolower (*ptr2);
		  if (strncmp (temp, "ftp://", 6))
		    {
		      mystring_append_char (&result, *ptr);
		      ptr++;
		    }
		  else
		    {
		      if (is_url_path (ptr + 6, &count))
			{
			  append_anchor ("ftp://", ptr + 6, count, &result);
			  for (i = 1; i <= (6 + count); i++)
			    {
			      mystring_append_char (&result, *ptr);
			      ptr++;
			    }
			  mystring_append (&result, "</a>");
			}
		      else
			{
			  mystring_append_char (&result, *ptr);
			  ptr++;
			}
		    }
		}
	    }
	  break;


	case 't':
	case 'T':
	  if (!do_slashes || !(ptr == line || (*(ptr - 1) == ' ')))
	    {
	      mystring_append_char (&result, *ptr);
	      ptr++;
	    }
	  else			/* we know there's a space before the 't' */
	    {
	      *temp = '\0';
	      strncat (temp, ptr, 14);	/* 14 = strlen("telnet://a.b.c") */
	      if (strlen (temp) != 14)
		{
		  mystring_append_char  (&result, *ptr);
		  ptr++;
		}
	      else
		{
		  for (ptr2 = temp; *ptr2; ptr2++)
		    *ptr2 = tolower (*ptr2);
		  if (strncmp (temp, "telnet://", 9))
		    {
		      mystring_append_char (&result, *ptr);
		      ptr++;
		    }
		  else
		    {
		      if (is_url_path (ptr + 9, &count))
			{
			  append_anchor ("telnet://", ptr + 9, count, &result);
			  for (i = 1; i <= (9 + count); i++)
			    {
			      mystring_append_char (&result, *ptr);
			      ptr++;
			    }
			  mystring_append (&result, "</a>");
			}
		      else
			{
			  mystring_append_char (&result, *ptr);
			  ptr++;
			}
		    }
		}
	    }
	  break;


	case 'm':
	case 'M':
	  if (!do_mail || !(ptr == line || (*(ptr - 1) == ' ')))
	    {
	      mystring_append_char (&result, *ptr);
	      ptr++;
	    }
	  else			/* we know there's a space before the 'm' */
	    {
	      *temp = '\0';
	      strncat (temp, ptr, 12);	/* 12 = strlen("mailto:a@b.c") */
	      if (strlen (temp) != 12)
		{
		  mystring_append_char (&result, *ptr);
		  ptr++;
		}
	      else
		{
		  for (ptr2 = temp; *ptr2; ptr2++)
		    *ptr2 = tolower (*ptr2);
		  if (strncmp (temp, "mailto:", 7))
		    {
		      mystring_append_char (&result, *ptr);
		      ptr++;
		    }
		  else
		    {
		      if (is_mail_address (ptr + 7, &count))
			{
			  append_anchor ("mailto:", ptr + 7, count, &result);
			  for (i = 1; i <= (7 + count); i++)
			    {
			      mystring_append_char (&result, *ptr);
			      ptr++;
			    }
			  mystring_append (&result, "</a>");
			}
		      else
			{
			  mystring_append_char (&result, *ptr);
			  ptr++;
			}
		    }
		}
	    }
	  break;

        case ' ': if(last_was_space)
                     mystring_append(&result, "&nbsp;");
                  else
                      mystring_append_char(&result, ' ');
                  ptr++;
                  break;

	default:
	          mystring_append_char (&result, *ptr);
	         ptr++;
	          break;
	}
     last_was_space = (examined_char == ' ');
    }
  }
*new_line = shrink_copy(result.buf);
mystring_free(&result);
}


/* does the string start with chars that typically start a url? * */

static
int starts_with_url(char *string)
{
#define MIN_LEN 8     /* 8 is strlen("https://")  */

 char *ptr;
 char saved;
 char string_start[MIN_LEN + 1];
   
 
  /* Assume that if the string is not at least as long as
  ** MIN_LEN, then it's not a URL.  This is a safe assumption
  ** because if the string WAS shorter than that, it wouldn't
  ** have wrapped in the first place.
  */
  if(strlen(string) < MIN_LEN)
     return 0;

  /* To facilitate the string comparisons, make a copy of 
  ** the beginning of string into string_start
  */
  saved = *(string + MIN_LEN);
  *(string +  MIN_LEN) = '\0';
  strncpy(string_start, string, MIN_LEN + 1);
  *(string + MIN_LEN) = saved;

  /* original string is back to where it started.
  ** string_start contains the first MIN_LEN chars
  ** of original string
  **/
  
  /* convert (small) string start to lowercase for comparison
  */  
  for(ptr=string_start; *ptr; ptr++)
     *ptr = tolower(*ptr);
   
  /* does string_start start with the magic characters */
        
  ptr = strstr(string_start,"https://");
  if(!ptr)
    {
      ptr = strstr(string_start,"http://");
      if(!ptr)
        {
             ptr = strstr(string_start,"ftp://");
         }
      }     
  return (ptr == string_start)? 1 : 0;

#undef MIN_LEN

}


/* Returns a char pointer to the character in line_start, which
** marks the beginning of a url that has been wrapped to another line,
** or (char *) 0 if no wrapping of a url has occurred in this line.
**
** line_start is the beginning of the line.
** line_end points to the end of the line and *line_end == '\n'
**/
static 
char *wrapped_url_start(char *line_start, char *line_end)
{
 char *url_start;
 char *space_ptr;
 int  is_url;
 

 /* A continued line will end with CONTINUATION_CHAR\n */ 
 if( (line_start >= line_end - 1) || (*(line_end - 1) != CONTINUATION_CHAR))
    return (char *) 0;    /* line doesn't end with CONTINUATION_CHAR */
    
 /* still here? Then the line MAY contain a wrapped URL */    
    
    
 *(line_end - 1) = '\0';   /* change CONTINUATION_CHAR to terminating NULL */

 /* The message we are working on may contain a quote from a 
 ** previous message.  We'll try to maintain the URL linking even
 ** in these quoted messages.
 ** Skip past any leading QUOTE_CHAR
 **/
 for(url_start = line_start; *url_start && (*url_start == QUOTE_CHAR); url_start++); /* skip leanding QUOTE_CHAR */



 if(!*url_start)  /* line had nothing but QUOTE_CHAR */
    {
      *(line_end - 1) = CONTINUATION_CHAR;   /* restore the string to its original contents */
       return (char *) 0;                    /* no wrapped url found */
    }   
    
    
  /* Still here? Then url_start points to the first character in the line
  ** that is not a QUOTE_CHAR.
  ** The start of a wrapped URL is either here, or it's after the first space
  ** looking in reverse from the end of the string
  **/     
       
 space_ptr = strrchr(url_start,' ');  /* search backwards for a space */
 if(space_ptr)
     url_start = space_ptr + 1;      
     
  /* At this point, url_start points to a string containing all non-blank characters
  ** This string was wrapped automatically when the message was sent.
  ** Does this string start with chars like "http://"    ???
  **/
 is_url = starts_with_url(url_start);

 *(line_end - 1) = CONTINUATION_CHAR;    /* restore original string */
 
 
 if(is_url)
     return url_start;
 else
     return (char *) 0;

}



     



/** Returns a newly allocated string which contains a url of the form:
***  "http://....." or "https:// ....." or "ftp://"
***
*** line_start :points to the start of the ENTIRE remainder of the 
***            message.  The wrapped url is known to start at:
***
*** start_wrapped_url : points to the start of the wrapped url.
***                     line_start <= start_wrapped_url < line_end
***            
*** line_end:     points to the first '\n' after line_start
***
*** wrapped_url is a newly allocated string containing the url, which
**              will be set by this function
***
*** wrapped_url_line_count is set by this function and represents the
***                        number of lines we had to process before
***                        finding the end of the wrapped url
***
*** This function returns a char pointer to the point in the original
*** message string that contained the last character of the wrapped_url
**/


static
char *set_wrapped_url(char *line_start, char *line_end, char *start_wrapped_url, 
                                             char **wrapped_url, int *wrapped_url_line_count)
{

 int url_max;
 char saved_char;
 char *dest_ptr;
 char *src_ptr;
 char *end_wrapped_url;
 char *tmp_wrapped_url;
 int start_of_line;
 int done;
 
 /* How much memory to allocate?  This may be wasteful, but
 ** we know the url cannot be longer then the length of the entire 
 ** message, starting from the beginning of this line.
 **
 ** This should allow no limits on the length of a wrapped URL.
 ** We'll shrink the allocated space down before returning.
 **/ 
 url_max = strlen(line_start) + 50;     /* this is actually the length of the entire msg, 
                                        ** plus a fudge factor, starting from line_start 
                                        */
 
 /** allocate space for the url */
 tmp_wrapped_url = (char *) malloc((url_max + 1) * sizeof(char));
 if(!tmp_wrapped_url)
     cs_critical_error(ERR_MALLOC_FAILED, "set_wrapped_url()");
     
 /* line_end points to the first '\n'
 ** the char before line_end is CONTINUATION_CHAR
 ** for simplicity, null terminate line_start at the CONTINUATION_CHAR
 */     
 saved_char = *(line_end -1 );   /* this SHOULD always be CONTINUATION_CHAR, or we wouldn't be here! */
 *(line_end - 1)  = '\0';
 
 
 
 /** ASSUMES start_wrapped_url has been correctly set
 **  by other routines to be before line_end.
 **  
 **  Since start_wrapped_url points to the start of the 
 **  URL, we can safely assume the wrapped_url starts here
 **  and extends to the end of the line
 */
 strncpy(tmp_wrapped_url,start_wrapped_url, url_max + 1);


 /* restore original passed-in string **/
  *(line_end - 1) = saved_char;
 

 /* We'll be adding a character at a time to the 
 ** wrapped_url we started.  Find the end of that
 ** string.
 */
 dest_ptr = strchr(tmp_wrapped_url, '\0');


 /* The above assignment should always find a null
 ** terminator.  To be safe, test and bail with 
 ** empty entries, which should be safe for the
 ** caller.
 **/
 
 if(!dest_ptr)   
    {
      *wrapped_url = (char *) malloc(sizeof(char));
      **wrapped_url = '\0';
      *wrapped_url_line_count = 0;
      return (char *) 0;
    }
      
      
 /** search forward through the message, building the 
 *** tmp_wrapped_url string as we go, by copying *src_ptr to 
 **  *dest_ptr, until we find a character
 *** that marks the end of the wrapped_url
 **/     
 done = 0;     
 src_ptr = line_end + 1;   /* start after that first '\n' */
 start_of_line = 1;        /* need to track when we are at the start of a line
                           ** to know if we need to skip leading QUOTE_CHAR
                           */
                           


 /* each iteration deals with exactly one character
 ** from the original message 
 */
 while(!done)
  {
   
   switch(*src_ptr)    /* what is that character? */
     {
      /* if it's a quote character at the start of a line,
      ** ignore it.  If it's NOT at the start of a line
      ** then we've found the end of the url
      **/
      case QUOTE_CHAR:  if(!start_of_line)
                         {
                          done = 1;
                          end_wrapped_url = src_ptr;   /* mark the char after the end of the url */
                         } 
                        break;
                        
                        
      /* If we encounter a CONTINUATION_CHAR at the end of a line
      ** it means this url spans yet another line.  If the 
      ** CONTINUATION_CHAR is NOT at the end of a line, then it
      ** represents the end of the URL.
      **/                  
      case CONTINUATION_CHAR:  if(*(src_ptr + 1) != '\n')
                                {
                                 done = 1;
                                 end_wrapped_url = src_ptr;
                                } 
                               else
                                {
                                 src_ptr++;    /* LOOK- this skips the '\n' */
                                 start_of_line = 1;
                                } 
                               break;           
              
      
      /** For any char other than QUOTE_CHAR and CONTINUATION_CHAR ***/
                              
      default:         if(isspace(*src_ptr))    /* any whitespace ends the url */
                         {
                           done = 1;
                           end_wrapped_url = src_ptr;
                         }  
                       else               /* it's not a space - add it to the URL */
                         {
                          *dest_ptr = *src_ptr;
                          dest_ptr++;
                          *dest_ptr = '\0';  /* leave dest_ptr -->  '\0' */
                          start_of_line = 0;
                         }
                        break;
     }
  if(!done)
     src_ptr++;
  }

 
 
  
 /* now count how many lines the url spanned
 ** it's always one more than the number of \n's encountered,
 ** so we start the count at one
 **/
 for(src_ptr = start_wrapped_url, *wrapped_url_line_count = 1; *src_ptr && (src_ptr < end_wrapped_url); src_ptr++)
   {
    if(*src_ptr == '\n') 
     (*wrapped_url_line_count)++;
   }  
   
   
/* we (probably) allocated much too much memory
** shrink it down to size
*/
 *wrapped_url = shrink_copy(tmp_wrapped_url);
 free(tmp_wrapped_url);
        
     
 return end_wrapped_url;                              
}                                                       
 
 



/** adds the null-terminated line to the linked list ***/
static 
void add_line_to_list(char *line, int reply_level)
{
 LINE_NODE *line_ptr;
 LINE_NODE *new_node;

 
 new_node = (LINE_NODE *) malloc(sizeof(LINE_NODE));
 if(!new_node)
     cs_critical_error(ERR_MALLOC_FAILED, "add_line_to_list()");
     
 new_node->content = line;
 new_node->reply_level = reply_level;
 new_node->next = (LINE_NODE *) 0;
 
 if(!line_head)
     line_head = new_node;
 else
  {
   for(line_ptr=line_head; line_ptr->next; line_ptr = line_ptr->next);
   line_ptr->next = new_node;
  }     
}
   

static 
int reply_level_count(char *string)
{
 char *ptr;
 int reply_level;
 
 for(ptr = string, reply_level = 0;
               *ptr && (*ptr == QUOTE_CHAR); ptr++, reply_level++);
 return reply_level;
}               
               


/** adds the line, which starts with line_start
**  to the list.
**  line_end points to a '\n' and marks the end
**  of the line.
**/
static
void add_formatted_line(char *line_start, char *line_end)
{
 char saved_char;
 char *formatted_line;
 int reply_level;
   
 saved_char = *line_end;
 *line_end = '\0';
 
 reply_level = reply_level_count(line_start);
               
 alloc_formatted_line(line_start, 1, 1, &formatted_line);
 *line_end = saved_char;
 
 add_line_to_list(formatted_line, reply_level);
 
}

static void
append_wrapped_anchor(char *wrapped_url, MYSTRING *thestring)
{
    mystring_append(thestring, "<a href=\"");
    mystring_append(thestring, wrapped_url);
    mystring_append(thestring, "\" onclick=\"newwin=window.open('");
    mystring_append(thestring, wrapped_url);
    mystring_append(thestring, "','link','menubar=yes,toolbar=yes,status=yes,scrollbars=yes,resizable=yes,width=760,height=500,location=yes'); newwin.focus(); return false\" >");

}


static void
add_wrapped_url_line(char *line_start, char *line_end, char *start_wrapped_url, 
                                                 char *end_wrapped_url, char *wrapped_url)
{

 char *formatted_line = 0;
 char saved_char;
 char *ptr;
 int reply_level;
 MYSTRING result;
   
 /* record number of leading QUOTE_CHARs before any formatting is 
 ** done 
 **/
 
 
 saved_char = *line_end;
 *line_end = '\0';
 reply_level = reply_level_count(line_start);
 *line_end = saved_char;

 /* start with a reasonable amount of space */
 mystring_init(&result, MINIMUM_SIZE * 2);

 
 /* If the start of the wrapped URL is on this line, then
 ** we know the URL part must extend to the end of the line.
 **
 **     1) Write the part before the URL (formatted)
 **     2) Write the url, which extends to the end of the line 
 **/
 if( (start_wrapped_url >= line_start) && (start_wrapped_url <= line_end))  
  {
    /* format from the beginning of the line to the start
    ** of the wrapped_url, and store that chunk in the 
    ** string
    */
    saved_char = *start_wrapped_url;
    *(start_wrapped_url) = '\0';
    
    /* line_start points to the start of the string and
    ** ends right before the start of the wrapped url
    ** Format this chunk, and store it
    */
    alloc_formatted_line(line_start, 1, 0, &formatted_line);
    if(formatted_line)
      {
        mystring_append(&result, formatted_line);
        free(formatted_line);
        formatted_line = 0;
      }
        
    *start_wrapped_url = saved_char;
    
    /* now store the hypertext <a href=".... "> anchor */    
    append_wrapped_anchor(wrapped_url, &result);

    /* write the text that's to be linked */
    saved_char = *line_end;
    *line_end = '\0';
    
    mystring_append(&result,start_wrapped_url);
    *line_end= saved_char;
    
    mystring_append(&result, "</a><br />");
  }
   
 /*** ELSE
 **** The wrapped URL began on an earlier line.
 **** The URL begins at the start of this line (after we skip
 **** leading QUOTE_CHARS.  The URL extends to the greater
 **** of line_end or end_wrapped_url
 ***/
 else 
   {
     /* process all leading QUOTE_CHARS
     */
     for(ptr = line_start; (ptr < line_end) && (*ptr == QUOTE_CHAR); ptr++);
     if(ptr != line_start)
        {
         saved_char = *ptr;
         *ptr = '\0';
         alloc_formatted_line(line_start, 1, 0, &formatted_line);
        /* add it to the string */
        if(formatted_line)
         {
           mystring_append(&result, formatted_line);
           free(formatted_line);
         }  
        *ptr = saved_char;   
        }
        
      /* move line_start to the begining of the url */
      
      line_start = ptr;
      
       /* write the html for the url */
           append_wrapped_anchor(wrapped_url, &result);

       
       /* the url either extends to line_end, or stops at end_wrapped_url */
       if(end_wrapped_url < line_end)
         {
          saved_char = *(end_wrapped_url + 1);   /* since end_wrapped_url points to the last char of url */
          *(end_wrapped_url + 1) = '\0';
          mystring_append(&result, line_start);           /* save the text to be hyperlinked */
          *(end_wrapped_url + 1) = saved_char;
          mystring_append(&result, "</a>");               /* end the hyperlink */
          
          /* save the remainder of the line as formatted */
          saved_char = *line_end;
          *line_end = '\0';
          alloc_formatted_line(end_wrapped_url, 1, 0, &formatted_line);
          if(formatted_line)
            {
             mystring_append(&result, formatted_line);
             free(formatted_line);
             formatted_line = 0;
            }
           *line_end = saved_char;
                       
         }
       else    /* url extends to the end of the line */
         {
           saved_char = *line_end;
           *line_end = '\0';
           mystring_append(&result, line_start);
           mystring_append(&result, "</a><br />");
           *line_end = saved_char;
         }  
         
   }                


  formatted_line = shrink_copy(result.buf);
  add_line_to_list(formatted_line, reply_level);
  mystring_free(&result);

}
 


/* Builds a linked list of formatted lines from original_msg
** assumes *original_msg is a NULL terminated string ending with a '\n'
**/

LINE_NODE * build_formatted_line_list(char *original_msg)
{
  /* we'll work on one line at a time ... */
  char *line_start;   /* points to the first char of the line */
  char *line_end;     /* points to the '\n' char at the end of the line */
  
  char *wrapped_url;       /* holds an http://... address that was wrapped across several lines */
  char *start_wrapped_url; /* points to the char in original_msg where the wrapped url starts */
  char *end_wrapped_url;   /* points to one char beyond the end of the wrapped url in original_msg */
  int wrapped_url_line_count;  /* the wrapped url extends across this number of lines */

  line_head = (LINE_NODE *) 0;   /* start with empty line list */
  
  line_start = original_msg;
  wrapped_url = (char *) 0;
  wrapped_url_line_count = 0;
  
  for(;;)
   {
     /* find the end of the next line to process
     ** Note that a line MUST end in a '\n'
     **/
     for(line_end = line_start; *line_end && (*line_end != '\n'); line_end++); 
     if(!*line_end)
       {
        break;    /* break from loop when we hit a null terminator */
       }
 
     /* if we're working on a line that contains a wrapped_url
     ** then process that line in a special way
     **/
     if(wrapped_url && wrapped_url_line_count)
          {
           /* add this line to the list, accounting for the fact that it at least
           ** partially contains a wrapped url 
           */
           add_wrapped_url_line(line_start, line_end, start_wrapped_url, end_wrapped_url, wrapped_url);
           
           wrapped_url_line_count--;     /* we've got one less wrapped_url line to deal with */
           if(!wrapped_url_line_count)   /* are we done with all wrapped_url lines ? */
               {
                 free(wrapped_url);          /* if yes, then free memory */
                 wrapped_url = (char *) 0;
               }
           line_start = line_end + 1;    
           }

         else          /* we don't have a wrapped_url stored */
          {

           /* If this line contains a the start of a wrapped url,
           ** then store the wrapped_url, count # of lines it spans,
           ** and record the place in original_msg where the wrapped 
           ** url ends.  Don't process this line now.  The next
           ** loop will handle it.
           */
           if((start_wrapped_url = wrapped_url_start(line_start, line_end)))
              {
               end_wrapped_url = set_wrapped_url(line_start, line_end, start_wrapped_url, 
                                                                &wrapped_url, &wrapped_url_line_count);
              } 

           else  /*this line does not contain a wrapped_url - handle it normally */
             {
              add_formatted_line(line_start, line_end);
              line_start = line_end + 1;
             } 
                  
          }
   }
 return line_head;
}
    


LINE_NODE * free_formatted_line_list()
{
 LINE_NODE *ptr;
 
 while(line_head)
  {
   ptr = line_head;
   line_head = line_head->next;
   if(ptr -> content)
       free(ptr->content);
   free(ptr);
  }
 return line_head;
}




static void
set_link_data(const char *name_prefix,  char *url, const char *inet_url)
{
 char url_url[(5 * MAX_URL) + 2];
 char tmp_url[MAX_URL *3 + 2];
 char *ptr;
 char *new_str;
 char name[50];
  
  strtrm (url);		 /* shared_strtrm.c */
  strncpy (tmp_url, url, MAX_URL*3 +2);

  for (ptr = tmp_url; *ptr; ptr++)
    switch (*ptr)
      {
      case '?':
	*ptr = QMARK_SUB;
	break;
      case '&':
	*ptr = AMP_SUB;
	break;
      case '=':
	*ptr = EQUAL_SUB;
	break;
      case '#':
        *ptr = POUND_SUB;
        break;	
      default:
	break;
      }

  snprintf
    (url_url, ( 5 * MAX_URL) + 2, "%s%s", inet_url, tmp_url);
  
  snprintf(name,50, "%sinet_url", name_prefix);
  cs_set_value(name, url_url);     /* shared_cs_util.h */

  cs_html_escape_string(url, &new_str);   /* shared_cs_util.c */
  if(new_str)
   {
     snprintf(name,50, "%sinet_link", name_prefix);
     cs_set_value(name, new_str); 	/* shared_cs_util.h */
     free(new_str);
   }  
}


static char * 
insert_smiley_faces(char *msg)
{
   int len;
   char *new_str =0;
   char *ptr, *ptr2;
   int i;
   char img_str[MAX_PATH +1];
   char *my_str =0;



  len = strlen(msg);
  ptr2 = msg;
 
  if(len)
  {
     for(i =0; i< MAX_SMILE_CODE; i++)
     {
         len = len *2;
         new_str = (char*)malloc(sizeof(char) * len +1);
         if(!new_str)
	     cs_critical_error(ERR_MALLOC_FAILED, "new_str");
         *new_str = '\0';
	 do{
             ptr = strstr(ptr2, code[i]);
             if(ptr)
	     {
	       *ptr = '\0';
               snprintf(img_str, MAX_PATH +1, "<img src=\"%s%s%d/%s\" alt=\"\"/>", 
			       system_data_str("IMAGE_PATH"), THEME_DIR_PREFIX, global_theme,icon_img[i]);
	       if((strlen(new_str) + strlen(ptr2) + strlen(img_str) )>= len)
	       {
	           len = strlen(new_str) + strlen(ptr2) + strlen(img_str);
		   new_str = realloc(new_str, len*2 +1);
		   len = len *2;
	        }
	       if(ptr2 != ptr)
	            strncat(new_str, ptr2, strlen(ptr2));
	       strncat(new_str, img_str, strlen(img_str));
	       ptr2 = ptr + strlen(code[i]); 
	       memset(img_str, 0, MAX_PATH +1);
	     }
            else
	    {
	        if((strlen(new_str) + strlen(ptr2) )>= len)
	        {
	            len = strlen(new_str) + strlen(ptr2);
		    new_str = realloc(new_str, len*2 +1);
		    len = len *2;
	         }
	         strncat(new_str, ptr2, strlen(ptr2));
	     }
     }while(ptr);
     
      if(my_str)
	   free(my_str);
       my_str = (char*)malloc(sizeof(char) * strlen(new_str) +1);
       memset(my_str, 0, strlen(new_str)+1);
       strncpy(my_str, new_str, strlen(new_str));
       free(new_str);
       ptr2 = my_str;	 	

    }
  } 
  return my_str;   
}

char *is_pair(char *start, const char *bb_code)
{
    char *ptr,  *ptr1;
    ptr = start + strlen(bb_code);
    if(isspace(*ptr))
	 return 0;
    do
    {
       ptr1 = strstr(ptr, bb_code);
       ptr = ptr1 + strlen(bb_code);
    }while(ptr1 && isspace(*(ptr1 -1)));
    if(ptr1 && !isspace(*(ptr1 -1)))
	  return ptr1;
    else
      return 0;
}
int count_new_line(char *start, char *end)
{
   int count =0;
   char *ptr;
   for(ptr = start; ptr < end; ptr++)
   {
        if( *ptr == '\n')
	   count++;
   }
   return count;
}

char *add_bb_tag_between_line(char *start, char *end, int bb_index, int new_line_count)
{
     char *ptr = 0;
     char *str;
     str = (char*)malloc(sizeof(char) * 
	((end - start) + strlen(start_tag_array[bb_index]) * new_line_count + strlen(end_tag_array[bb_index]) * new_line_count +1)); 
     if(!str)
	  cs_critical_error(ERR_MALLOC_FAILED, "add_bb_tag_between_line");
    *str = '\0';
    for(ptr = start; ptr < end; ptr++)
    {
       if(*ptr == '\r' && *(ptr +1) == '\n')
       {
	   strncat(str, end_tag_array[bb_index], strlen(end_tag_array[bb_index]));
           strncat(str, ptr, 2);
	   ptr++; 
	   if(*(ptr +1) == '>')
	   {
	     ptr = ptr +1;
	     do
	     {
	        strncat(str, ptr, 1);
		ptr = ptr + 1;
	     }while(*ptr == '>');
	     ptr = ptr -1;
	   }
	   strncat(str, start_tag_array[bb_index], strlen(start_tag_array[bb_index]));
       }
       else if(*ptr == '\n')
       {
	   strncat(str, end_tag_array[bb_index], strlen(end_tag_array[bb_index]));
           strncat(str, ptr, 1);
	   if(*(ptr +1) == '>')
	   {
	     ptr = ptr +1;
	     do
	     {
	        strncat(str, ptr, 1);
		ptr = ptr +1;
	     }while(*ptr == '>');
	     ptr = ptr -1;
	   }
	   strncat(str, start_tag_array[bb_index], strlen(start_tag_array[bb_index]));
       }
       else
           strncat(str, ptr, 1);
    }
    return str;
}

static
char *bbcode2html(char *buffer)
{
    char *new_str, *ptr, *ptr1 =0, *ptr2 =0, *ptr3;
    int i, len, sub_len, new_line_count =0;
    char *my_str =0, *add_bb_str =0;

    len = strlen(buffer);
    ptr = buffer;
    if(len)
    {
         for(i =0; i<MAX_BB_CODE; i++)
	 {
             len = len * 2;
	     new_str = (char *)malloc(sizeof(char) *len + 1);
	     if(!new_str)
		 cs_critical_error(ERR_MALLOC_FAILED, "");
	     *new_str = '\0';
	     do
	     {
	       ptr1 = strstr(ptr, bb_code_array[i]);
	       if(ptr1)
		  ptr2 = is_pair(ptr1, bb_code_array[i]);
	       if(ptr1 && ptr2)
	       {
		  *ptr1 = '\0';
		   ptr3 = ptr1 + strlen(bb_code_array[i]);
                   new_line_count = count_new_line(ptr3, ptr2);

		   sub_len = strlen(new_str) + strlen(ptr) + 
		       strlen(start_tag_array[i]) *(new_line_count + 1) + 
		       (ptr2 - (ptr1+ strlen(bb_code_array[i]))) + 
		       strlen(end_tag_array[i]) * (new_line_count + 1) ;
		  if(sub_len >=len)
		  {
		    len = sub_len;
		    new_str = realloc(new_str, len * 2 + 1);
		    len = len*2;
		  }
                  if(ptr1 != ptr)
		       strncat(new_str, ptr, strlen(ptr));
		  strncat(new_str, start_tag_array[i], strlen(start_tag_array[i]));
		  if(new_line_count)
		  {
		     add_bb_str = add_bb_tag_between_line(ptr3, ptr2, i,  new_line_count);
                     if(add_bb_str)
		     {
		       strncat(new_str, add_bb_str, strlen(add_bb_str));
		       free(add_bb_str); 
		     }
		  }
		  else
		    strncat(new_str, ptr3, (ptr2 -ptr3));
		  strncat(new_str, end_tag_array[i], strlen(end_tag_array[i])); 
		  ptr = ptr2 + strlen(bb_code_array[i]);

	       }
	       else
	       {
                   sub_len = strlen(ptr) + strlen(new_str);
		   if(sub_len >= len)
		   {
		    len = sub_len;
		    new_str = realloc(new_str, len*2 +1);
		    len = len*2;
		   }
                   strncat(new_str, ptr, strlen(ptr));
	       } 
	     }while(ptr1 && ptr2);
           if(my_str)
		free(my_str);
	  my_str = (char*) malloc(sizeof(char) * strlen(new_str) +1);
          memset(my_str, 0, strlen(new_str)+1);
          strncpy(my_str, new_str, strlen(new_str));
          free(new_str);
	  ptr = my_str;

	 }

    } 
    return my_str;
}



/** Some will paste from MS Word into the textarea for messages.
*** Along with plain ascii, comes lots of junk, primarily 'smart quotes'
*** This function attempts to find and destroy these smart quote characters
***
*** used http://www.cs.tut.fi/~jkorpela/www/windows-chars.html as a reference
**/

void
remove_smart_quotes(char *buffer)
{
 char *ptr;
 
 for(ptr = buffer; *ptr; ptr++)
  {
   switch((unsigned char) *ptr)
      {
       case 130:   *ptr = '\'';  break;    /* basline single quote */
       case 132:   *ptr = '"';   break;    /* baseline double quote  */
       case 133:   *ptr = '.';   break;    /* this is a horizontal ellipsis - replace with single period */
       
       case 145:
       case 146:   *ptr = '\'';  break;    /* left, and right single quote */
       
       case 147:
       case 148:   *ptr = '"';   break;    /* left, and right double quotes */
       
       case 149:   *ptr = '*';   break;    /* bullet */
       
       case 150:
       case 151:  *ptr = '-';    break;    /* endash, emdash */
       
       
       /* replace sequence of 226 128 147 with <space>-<space>
       ** This is an MS Word special symbol for a dash
       */
       case 226:  if( (*(ptr + 1) == -128) && 
                      (*(ptr + 2) == -109 ) )
                    {
                      *ptr = ' ';
                      ptr++;
                      *ptr = '-';
                      ptr++;
                      *ptr = ' ';
                    }
                   break;   
                 
       default:  break;
       
      }
   } 
}

   
       

void
set_msg_body_data (const char *fullpath, const char *name_prefix, const char *inet_url)
{
  FILE *fp;

  char *buffer = 0;            /* stores the entire content of the msg file */	
  struct stat stat_buf;        /* used for fstat() call */
  char *ptr;
  char *buffer_icon = 0;
  char *buffer_bb = 0;

  char *url, one_char;
  int len, i, big = MAX_URL;

  LINE_NODE *l_head;   /* LINE_NODE is in shared_msg_line.h */
  LINE_NODE *l_ptr;
  
  int line;
  char hdf_content_name[50];
  char hdf_reply_level_name[50];
  char temp[MAX_PATH +1];
  
  fp = fopen (fullpath, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, fullpath);  /* shared_cs_util.c */
    
 if(fstat(fileno(fp),&stat_buf))
     cs_critical_error(ERR_STAT_FAILED, fullpath);   /* shared_cs_util.c */
    
    
   if(*inet_url)
   {
       url = (char*) malloc(sizeof(char) * (MAX_URL+1));
       if(!url)
          cs_critical_error(ERR_MALLOC_FAILED, "url");
       *url = '\0';

     if(strcmp(inet_url, "testpilot") == 0)
     {
        fgets(url, MAX_PATH, fp);
        strtrm(url);
	snprintf(temp, MAX_PATH +1, "%stestpilot_tp4", name_prefix);
        cs_set_value(temp, url);
        stat_buf.st_size = stat_buf.st_size - strlen(url) - 1;   /* since set_link_data() might change url */
        free(url);	
     }
     else
     {
       i = 0;

       while((one_char = fgetc(fp)) != '\n')
       {
          len = strlen(url);
	  if(len >= big)
	  {
	     url = realloc(url, (big * 2) + 1);
	     big = big * 2;
	  }
          url[i] = one_char;
          url[i + 1] = '\0';
          i++;	
       } 
       stat_buf.st_size = stat_buf.st_size - strlen(url) - 1;   /* since set_link_data() might change url */
       set_link_data(name_prefix,url, inet_url);
     
       free(url);
     }
   }
     
  /* handle empty message */
  if(!stat_buf.st_size)
     {
      fclose(fp);
      return;
     }    
        
  buffer = (char *) malloc((stat_buf.st_size + 2) * sizeof(char));
  if(!buffer)
     cs_critical_error(ERR_MALLOC_FAILED, "set_msg_body_data()");
  if(fread(buffer, stat_buf.st_size, 1, fp) != 1)
      cs_critical_error(ERR_FREAD_FAILED, fullpath);
  fclose(fp);
      

  ptr = buffer+stat_buf.st_size - 1;
  if(*ptr != '\n')
    {
     ptr++;
     *ptr = '\n';
    }
  *(ptr + 1) = '\0';       
  

  remove_smart_quotes(buffer);
  
  buffer_icon = insert_smiley_faces(buffer);
  if(buffer)
     free(buffer);

  
  buffer_bb = bbcode2html(buffer_icon);
  if(buffer_icon)
     free(buffer_icon);
     
  l_head = build_formatted_line_list(buffer_bb);		
  for(l_ptr = l_head, line= 0; l_ptr; l_ptr=l_ptr->next, line++)
   {
      snprintf(hdf_content_name, 50, "%sline.%d.content", name_prefix, line);
      cs_set_value(hdf_content_name,l_ptr->content);			/* shared_cs_util.h */
     
    if(l_ptr->reply_level)  
     {
      snprintf(hdf_reply_level_name, 50, "%sline.%d.reply_level", name_prefix,line);
      cs_set_int_value(hdf_reply_level_name, l_ptr->reply_level);	/* shared_cs_util.h */
     } 
   }

  l_head=free_formatted_line_list();  

  if(buffer_bb)
     free(buffer_bb);

}





