#include <stdio.h>
#include <time.h>
#include <string.h>
#include <ctype.h>		/* for tolower() */
#include <sys/stat.h>		/* for stat() */

#include "global.h"
#include "custom.h"		/* for date FORMAT strings */

#include "manhat-lib/shared_util.h"
#include "manhat-lib/shared_cs_util.h"
#include "manhat-lib/shared_news_util.h"
#include "manhat-lib/shared_lock.h"
#include "manhat-lib/shared_server_string.h"
#include "manhat-lib/shared_mime_type.h"
#include "manhat-lib/shared_podcast_util.h"
#include "manhat-lib/shared_access.h"

ATTACHMENT_NODE *attachment_head = (ATTACHMENT_NODE *) 0;

static void
read_parameters (char *course, char *podcast_key)
{
  cs_get_required_parameter ("crs", course, MAX_FILENAME);
  cs_get_required_parameter ("id", podcast_key, MAX_KEY);
}




static void
memory_error ()
{
  cs_critical_error (ERR_MALLOC_FAILED, "");
};



static void
free_attachment_list()
{
 ATTACHMENT_NODE *ptr;

 while(attachment_head)
  {
   ptr = attachment_head;
   attachment_head = attachment_head -> next;
   free(ptr);
  }

}   



static void
build_attachment_list (MSG_TREE_NODE * msg, CONFIG_STRUCT *conf)
{
  FILE *fp;
  int fd;
  char infopath[MAX_PATH + 1];
  int i;
  ATTACHMENT_NODE *aptr = 0;
  NEWS_INFO info;
  int n_attachments;
  
  free_attachment_list();
  
  if(!msg->data.info.attachments)
     return;
  
  n_attachments = abs(msg->data.info.attachments);  /* since a -1 is used to flag an attached website */
  
  snprintf (infopath, MAX_PATH + 1, "%s%s/%s/%s/%s", 
                                    conf->course_path,
		                    PEOPLE_DIR,
		                    msg->data.info.sender,
	    			    discussion_fname, 
	    			    msg->data.info_fname);

  fp = fopen (infopath, "r");
  if (!fp)
    cs_critical_error (ERR_FOPEN_READ_FAILED, infopath);
  fd = fileno(fp);  
  get_shared_lock(fd, 1);                      /* shared_lock.c */   
 
  if (fread (&info, sizeof (NEWS_INFO), 1, fp) != 1)
    cs_critical_error (ERR_FREAD_FAILED, infopath);

  /* process attachments, build linked list */

  for (i = 1; i <= n_attachments; i++)
    {
      if (!attachment_head)
	{
	  attachment_head = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!attachment_head)
	          memory_error ();
	  aptr = attachment_head;
	}
      else
	{
	  aptr->next = (ATTACHMENT_NODE *) malloc (sizeof (ATTACHMENT_NODE));
	  if (!aptr->next)
	    memory_error ();
	  aptr = aptr->next;
	}

      if (fread (&aptr->data, sizeof (ATTACHMENT), 1, fp) != 1)
	cs_critical_error (ERR_FREAD_FAILED, infopath);
      aptr->next = 0;
    }				/* for .. */

release_lock(fd);                             /* shared_lock.c */
fclose(fp);

}


static void
get_attachment_size(CONFIG_STRUCT *conf, const char *sender, const char *unique_fname, long *file_size)
{
 char path[MAX_PATH + 1];
 struct stat file_stat;

 snprintf (path, MAX_PATH + 1, "%s%s/%s/%s/%s", 
                                    conf->course_path,
		                    PEOPLE_DIR,
		                    sender,
	    			    discussion_fname, 
	    			    unique_fname);
	    			    
 if(stat(path, &file_stat))
     *file_size = 0;
 else
      *file_size = file_stat.st_size;   
}	    			    

  



static
void send_xml_escaped_string(char *string)
{
 char *ptr;
 
  if(string)
   {
    for(ptr=string; *ptr; ptr++)
      switch(*ptr)
       {
        case '<': fputs("&lt;", stdout);
                  break;

        case '>': fputs("&gt;", stdout);
                  break;

        case '&': fputs("&amp;", stdout);
                  break;
 
        case '"': fputs("&quot;", stdout);
                  break;

        case '\'': fputs("&apos;", stdout);
                   break;

        default:  fputc(*ptr, stdout);
                  break;
        } 
     }   
 }
 
 
 


                  
static void 
send_description(const char *file)
{
    struct stat file_stat;
    FILE *fp;
    char *buf = 0; 
    
    fp = fopen (file, "r");
    if (!fp)
          cs_critical_error (ERR_FOPEN_READ_FAILED, file);  /* shared_cs_util.c */

    if(fstat(fileno(fp),&file_stat))
          cs_critical_error(ERR_STAT_FAILED, file);   /* shared_cs_util.c */
    if(file_stat.st_size > 0)
     {
       buf = (char*) malloc( sizeof(char)*(file_stat.st_size +1));
       if(!buf)
          cs_critical_error(ERR_MALLOC_FAILED, file);   /* shared_cs_util.c */
   
       if(fread(buf, file_stat.st_size, 1, fp) != 1)
          cs_critical_error(ERR_FREAD_FAILED, file);   /* shared_cs_util.c */
       fclose(fp);
       buf[file_stat.st_size]='\0';
     }
    else
     {
      buf = (char*) malloc( sizeof(char)*(2));
      *buf = '\0';
     }


    puts("<description>");
    send_xml_escaped_string(buf);
    puts("</description>");       

   if(buf)
       free(buf);
}









void send_one_podcast(MSG_TREE_NODE *msg_ptr, CONFIG_STRUCT *conf, const char *course, const char *server_string,
                                const char *podcast_key)
{

   char msg_path[MAX_PATH +1];
   char extension[MAX_FILENAME +1];
   char timestring[MAX_TIMESTRING +1];
   char mime_type[MAX_MIME_TYPE + 1];
   long attachment_size;
   
   build_attachment_list (msg_ptr, conf);

   puts  ("<item>");
   
   puts("<title>");
   send_xml_escaped_string(msg_ptr->data.info.subject);
   puts("</title>");
   

  if(attachment_head)
   {
      get_extension(attachment_head->data.orig_fname, extension);   /* shared_news_util.c */
      get_mime_type_from_extension(extension, mime_type);           /* shared_mime_type.c */
      get_attachment_size(conf,msg_ptr->data.info.sender, attachment_head->data.unique_fname, &attachment_size);

      /** Following URL will not work in iTunes and some other podcast aggregators
      *** if & is escaped as &amp; !!!! 
      */      

      printf("<enclosure url=\"%s/%s/podcast_send_file.%s?crs=%s&#38;id=%s&#38;fname=%s&#38;sender=%s&#38;inf=%s&#38;ext=.%s\" length=\"%ld\" type=\"%s\"  />\n", 
                   server_string, 
                   ALIAS, 
                   extension,
                   course, 
                   podcast_key,
                   attachment_head->data.unique_fname, 
                   msg_ptr->data.info.sender, 
                   msg_ptr->data.info_fname, 
                   extension,
                   attachment_size,
                   mime_type);

   }
   


 
   snprintf (msg_path, MAX_PATH + 1, "%s%s/%s/%s/%s",
	    conf->course_path, PEOPLE_DIR, msg_ptr->data.info.sender, discussion_fname, msg_ptr->data.info.msg_fname);
  
   send_description(msg_path);
   
   
   /* guid is an optional sub-element of <item>
   **
   ** guid stands for globally unique identifier.  It's a string that uniquely identifies the item.
   ** When present, an aggregator may choose to use this string to determin if an item is new.
   **
   ** There are no rules for the syntax of a guid.  Aggregators mus view them as a string.  It's up
   ** to the source of the feed to establish the uniqueness of the string.
   **
   ** If the guid element has an attribute "isPermaLink" with a value of true, the reader may assume
   ** that it is a permalink to the item, that is, a url that can be opened in a web browser, that 
   ** points to the full item described by the <item> element.
   **
   ** isPermaLink is optional, its default value is true.  If its value is false, the guid may not be
   ** assumed to be a url, or a url to anything in particular.
   **
   ** In our case, we can't have a URL to the <item>, since the user would have to login.  Set isPermaLink
   ** to false, and set the string to the course and offset of the message.  That should be sufficient
   ** for an aggregator to figure out if the item is new
   */
   
   printf("<guid isPermaLink=\"false\">%s %ld</guid>\n", course, msg_ptr->offset);
   
 
   /* Must use RFC 822 format for this date - #defined in custom.h */
   strftime (timestring, MAX_TIMESTRING, RFC822_DATE_FORMAT,localtime (&msg_ptr->data.info.time_sent));
   printf("<pubDate>%s</pubDate>\n", timestring);

   puts("</item>");

}







void
send_channel_data(CONFIG_STRUCT *conf, const char *course, const char *server_string)
{

   puts  ("<channel>");
   
   /* REQUIRED 
   * title - The name of your channel.  It's how people refer to your service.
   * If you have an HTML website that contains the same information as your RSS
   * file, the title of your channel should be the same as the title of your 
   * website.
   */
   printf("<title>" );
   send_xml_escaped_string(conf->course_no);
   putc(' ', stdout);
//   send_xml_escaped_string(conf->title);
//   putc(' ', stdout);
   send_xml_escaped_string(conf->semester);
   putc(' ', stdout);
   send_xml_escaped_string(conf->instructor);
   puts("</title>");
   


  /*  REQUIRED
   * link - The URL to the HTML website corresponding to the channel. 
   *
   *  If this is a standalone course, provide a direct login link to the course,
   *  otherwise let's use the EXIT_URL as #defined in custom.h  (hopefully this
   *  will have a login form and/or instructions
   */
   if(conf->standalone)
       printf("<link>%s/%s/doorstep?class=%s</link>\n", server_string, ALIAS, course);
   else
      {
       printf("<link>");
       send_xml_escaped_string(EXIT_URL);   /* #defined in custom.h */
       puts("</link>");
      } 
    
    
   /* REQUIRED 
   *  description - Phrase or sentence describing the channel 
   */       
   
       
   puts("<description>");
   send_xml_escaped_string(conf->course_no);
   putc(' ', stdout);
      send_xml_escaped_string(conf->semester);
   putc(' ', stdout);
   send_xml_escaped_string(conf->title);
   putc(' ', stdout);
   send_xml_escaped_string(conf->instructor);
   puts("</description>");
   
   
   
   
   
   /* OPTIONAL
   ** generator - A string indicating the program used to generate the channel
   **/
   
   printf("<generator>The Manhattan Virtual Classroom - ");
   send_xml_escaped_string(VERSION);   /* defined in global.h */
   puts("</generator>");
   
   
}





static void
send_podcast_rss_data (CONFIG_STRUCT *conf, SESSION *user, const char *course, const char *podcast_key)
{
  MSG_TREE_NODE *msg_ptr;
  time_t current_time;
  char server_string[MAX_PATH + 1];

    
  /* current_time is declared globally */
  build_msg_tree(user, PODCASTS, 0, 0, &current_time);   /* shared_news_util.c */
 
  
  puts ("Content-type:text/xml\n");
  puts ("<?xml version=\"1.0\" encoding=\"iso-8859-1\" ?>");
  puts ("<rss version=\"2.0\">");

  get_server_string(server_string, MAX_PATH +1);
  
  send_channel_data(conf, course, server_string);


  for(msg_ptr=tree_head; msg_ptr; msg_ptr=msg_ptr->next)
           send_one_podcast(msg_ptr, conf, course, server_string, podcast_key);


  puts("</channel>"); 
  puts("</rss>"); 
  
  free_msg_tree (tree_head);                     /* shared_news_util.c */

}



/** This program does no authentication, yet we need a SESSION user
*** This function sets up a 'dummy' user as a student.
**/
static void
get_dummy_student_user(SESSION *user)
{
 strncpy(user->username, "_unknown_", MAX_USERNAME + 1 );
 strncpy(user->realname, "_unknown_", MAX_NAME + 1);
 strncpy(user->id,"_unknown_id_", MAX_ID + 1);
 strncpy(user->alias, "_unknown_", MAX_NAME + 1);
 user->team = '!';
 user->group = STUDENT;
 user->expires = 0;
}





int
main (void)
{
 SESSION user;
 CONFIG_STRUCT conf;
 char podcast_key[MAX_KEY + 1];  /* from id=? command line */
 char course[MAX_PATH + 1];	/* from crs=? command line */ 
 
  cs_cgi_init(); 

  read_parameters (course, podcast_key); 
  read_configuration_file (course, &conf);	/* shared_util.c */

  validate_podcast_key(podcast_key, &conf);     /* shared_podcast_util.c */
  
  /* since we're not really authenticating, fill in some dummy values for 
  ** a student that can safely be used throughout this program
  */
  get_dummy_student_user(&user);

  has_write_permission(&user, &conf);   /* shared_access.c - access is denied if user can't view this course */
  set_discussion_names (conf.course_path, user.username, PODCASTS);	/* shared_news_util.c */

  send_podcast_rss_data (&conf, &user, course, podcast_key);

  cs_cgi_destroy();
  return 0;
}


