// rdcastmanager.cpp
//
// An RSS Feed Generator for Rivendell.
//
//   (C) Copyright 2002-2007 Fred Gleason <fredg@paravelsystems.com>
//
//      $Id: rdcastmanager.cpp,v 1.7 2007/12/24 15:56:13 fredg Exp $
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License version 2 as
//   published by the Free Software Foundation.
//
//   This program is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public
//   License along with this program; if not, write to the Free Software
//   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>

#include <qapplication.h>
#include <qdatetime.h>

#include <rd.h>
#include <rdconf.h>
#include <rdconfig.h>
#include <rdpodcast.h>
#include <rddb.h>
#include <rdweb.h>
#include <rdescape_string.h>
#include <rdfeed.h>

#include <rdcastmanager.h>


MainObject::MainObject(QObject *parent,const char *name)
  :QObject(parent,name)
{
  //
  // Initialize Variables
  //
  cast_session_id=-1;
  cast_feed_id=-1;
  cast_cast_id=-1;
  cast_add_priv=false;
  cast_edit_priv=false;
  cast_delete_priv=false;

  //
  // Read Configuration
  //
  RDConfig *config=new RDConfig();
  config->load();

  //
  // Open Database
  //
  QSqlDatabase *db=QSqlDatabase::addDatabase(config->mysqlDriver());
  if(!db) {
    printf("Content-type: text/html\n\n");
    printf("rdfeed: unable to initialize connection to database\n");
    exit(0);
  }
  db->setDatabaseName(config->mysqlDbname());
  db->setUserName(config->mysqlUsername());
  db->setPassword(config->mysqlPassword());
  db->setHostName(config->mysqlHostname());
  if(!db->open()) {
    printf("Content-type: text/html\n\n");
    printf("rdfeed: unable to connect to database\n");
    db->removeDatabase(config->mysqlDbname());
    exit(0);
  }

  //
  // Read Post Variables and Dispatch 
  //
  switch(AuthenticatePost()) {
    case RDCASTMANAGER_COMMAND_LOGIN:
      ServeLogin();
      break;

    case RDCASTMANAGER_COMMAND_LOGOUT:
      ServeLogout();
      break;

    case RDCASTMANAGER_COMMAND_LIST_FEEDS:
      ServeListFeeds();
      break;

    case RDCASTMANAGER_COMMAND_LIST_CASTS:
      ServeListCasts();
      break;

    case RDCASTMANAGER_COMMAND_EDIT_CAST:
      ServeEditCast();
      break;

    case RDCASTMANAGER_COMMAND_COMMIT_CAST:
      CommitCast();
      break;

    case RDCASTMANAGER_COMMAND_CONFIRM_DELETE_CAST:
      ConfirmDeleteCast();
      break;

    case RDCASTMANAGER_COMMAND_DELETE_CAST:
      DeleteCast();
      break;

    case RDCASTMANAGER_COMMAND_SUBSCRIPTION_PICK_DATES:
      ServeDatePicker(RDCASTMANAGER_COMMAND_SUBSCRIPTION_REPORT);
      break;

    case RDCASTMANAGER_COMMAND_SUBSCRIPTION_REPORT:
      ServeSubscriptionReport();
      break;

    case RDCASTMANAGER_COMMAND_EPISODE_PICK_DATES:
      ServeDatePicker(RDCASTMANAGER_COMMAND_EPISODE_REPORT);
      break;

    case RDCASTMANAGER_COMMAND_EPISODE_REPORT:
      ServeEpisodeReport();
      break;

    default:
      RDCgiError("Invalid post data!");
      break;
  }

  exit(0);
}


int MainObject::AuthenticatePost()
{
  int cmd=0;
  char str[1024];

  RDReadPost(cast_post,RDCASTMANAGER_POST_SIZE);

  //
  // Check for Command Value
  //
  if(RDGetPostInt(cast_post,"COMMAND",&cmd)<0) {
    return RDCASTMANAGER_COMMAND_LOGIN;
  }

  //
  // Validate Remote Address
  //
  if(getenv("REMOTE_ADDR")==NULL) {
    RDCgiError("Invalid CGI request!");
    exit(0);
  }
  cast_client_addr.setAddress(getenv("REMOTE_ADDR"));
  if(cast_client_addr.isNull()) {
    RDCgiError("Invalid client IP address!");
    exit(0);
  }

  //
  // Authenticate New Login
  //
  if((RDGetPostLongInt(cast_post,"SESSION_ID",&cast_session_id))<0) {
    if((RDGetPostString(cast_post,"LOGIN_NAME",str,1024))<0) {
      return RDCASTMANAGER_COMMAND_LOGIN;
    }
    cast_login_name=str;
    if((RDGetPostString(cast_post,"PASSWORD",str,1024))<0) {
      return RDCASTMANAGER_COMMAND_LOGIN;
    }
    if((cast_session_id=
	RDAuthenticateLogin(cast_login_name,str,cast_client_addr))<0) {
      return RDCASTMANAGER_COMMAND_LOGIN;
    }
    GetContext();
    return cmd;
  }

  //
  // Authenticate Existing Session
  //
  cast_login_name=RDAuthenticateSession(cast_session_id,cast_client_addr);
  if(cast_login_name.isEmpty()) {
    return RDCASTMANAGER_COMMAND_LOGIN;
  }
  GetContext();

  return cmd;
}


void MainObject::ServeLogin()
{
  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Login</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<input type=\"hidden\" name=\"COMMAND\" value=\"%d\">",
	 RDCASTMANAGER_COMMAND_LIST_FEEDS);

  printf("<tr>\n");
  printf("<td align=\"center\" colspan=\"2\"><big>RDCastManager Login</big></td>\n");
  printf("</tr>\n");

  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Login Name:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\"  align=\"left\"><input type=\"text\" name=\"LOGIN_NAME\" length=\"8\" maxlength=\"8\"></td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("</tr>\n");

  printf("<tr>\n");
  printf("<td bgcolor=\"%s\"  align=\"right\">Password:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\"  align=\"left\"><input type=\"password\" name=\"PASSWORD\" length=\"32\" maxlength=\"32\"></td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("</tr>\n");

  printf("<tr>\n");
  printf("<td colspan=\"2\">&nbsp;</td>\n");
  printf("</tr>\n");

  printf("<tr>\n");
  printf("<td>&nbsp;</td>\n");
  printf("<td align=\"right\"><input type=\"submit\" value=\"OK\"></td>\n");
  printf("</tr>\n");

  printf("</form>\n");
  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::ServeLogout()
{
  RDLogoutSession(cast_session_id,cast_client_addr);
  ServeLogin();
}


void MainObject::ServeListFeeds()
{
  QString sql;
  RDSqlQuery *q;
  RDSqlQuery *q1;
  int total=0;
  int active=0;

  cast_key_name="";
  cast_feed_id=-1;
  cast_cast_id=-1;

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Feed List</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  //
  // Title Section
  //
  TitleSection("Available Feeds",RDCASTMANAGER_COMMAND_LIST_FEEDS,3);

  //
  // Column Headers
  //
  printf("<tr>\n");
  printf("<th>&nbsp;</th>\n");
  printf("<th>KEY NAME</th>\n");
  printf("<th>FEED NAME</th>\n");
  printf("<th>CASTS</th>\n");
  printf("<th>&nbsp;</th>\n");
  printf("</tr>\n");

  //
  // Data Rows
  //
  QString line_colors[2];
  line_colors[0]=RD_WEB_LINE_COLOR1;
  line_colors[1]=RD_WEB_LINE_COLOR2;
  int current_color=0;

  sql=QString().sprintf("select FEED_PERMS.KEY_NAME from \
                         FEED_PERMS left join WEB_CONNECTIONS \
                         on(FEED_PERMS.USER_NAME=WEB_CONNECTIONS.LOGIN_NAME) \
                         where WEB_CONNECTIONS.SESSION_ID=%ld",
			cast_session_id);
  q=new RDSqlQuery(sql);
  sql=QString().sprintf("select ID,KEY_NAME,CHANNEL_TITLE from FEEDS \
                         where ");
  while(q->next()) {
    sql+=QString().sprintf("(KEY_NAME=\"%s\")||",
			   (const char *)q->value(0).toString());
  }
  delete q;
  if(sql.right(2)=="||") {
    sql=sql.left(sql.length()-2);
    sql+=" order by KEY_NAME";
    q=new RDSqlQuery(sql);
    while(q->next()) {
      total=0;
      active=0;
      printf("<tr>\n");
      sql=QString().sprintf("select STATUS from PODCASTS where FEED_ID=%u",
			    q->value(0).toUInt());
      q1=new RDSqlQuery(sql);
      while(q1->next()) {
	total++;
	switch((RDPodcast::Status)q1->value(0).toUInt()) {
	  case RDPodcast::StatusActive:
	  case RDPodcast::StatusExpired:
	    active++;
	    break;

	  case RDPodcast::StatusPending:
	    break;
	}
      }
      delete q1;
      if(active==total) {
	printf("<td bgcolor=\"%s\"><img src=\"greencheckmark.png\" border=\"0\"></td>\n",(const char *)line_colors[current_color]);
      }
      else {
	printf("<td bgcolor=\"%s\"><img src=\"redx.png\" border=\"0\"></td>\n",
	       (const char *)line_colors[current_color]);
      }
      printf("<td align=\"center\" bgcolor=\"%s\">%s</td>\n"
	     ,(const char *)line_colors[current_color],
	     (const char *)q->value(1).toString());
      printf("<td bgcolor=\"%s\">%s</td>\n",
	     (const char *)line_colors[current_color],
	     (const char *)RDEscapeWebString(q->value(2).toString()));
      printf("<td align=\"center\" bgcolor=\"%s\">%d / %d</td>\n",
	     (const char *)line_colors[current_color],active,total);
      printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
      printf("<td bgcolor=\"%s\">\n",(const char *)line_colors[current_color]);
      SetContext(RDCASTMANAGER_COMMAND_LIST_CASTS);
      printf("<input type=\"hidden\" name=\"FEED_ID\" value=\"%d\">\n",
	     q->value(0).toInt());
      printf("<input type=\"hidden\" name=\"KEY_NAME\" value=\"%s\">\n",
	     (const char *)q->value(1).toString());
      printf("<input type=\"submit\" value=\"View Feed\">\n");
      printf("</td>\n");
      printf("</form>\n");
      printf("</tr>\n");
      if(current_color==0) {
	current_color=1;
      }
      else {
	current_color=0;
      }
    }
    delete q;
  }

  //
  // Logout Button
  //
  printf("<tr><td colspan=\"5\">&nbsp;</td></tr>\n");
  printf("<tr>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"center\" colspan=\"4\">&nbsp;</td>\n");
  printf("<td align=\"center\">\n");
  printf("<input type=\"hidden\" name=\"COMMAND\" value=\"%d\">\n",
	 RDCASTMANAGER_COMMAND_LOGOUT);
  printf("<input type=\"hidden\" name=\"SESSION_ID\" value=\"%ld\">\n",
	 cast_session_id);
  printf("<input type=\"submit\" value=\"Logout\">\n");
  printf("</td>\n");
  printf("</form>\n");
  printf("</tr>\n");

  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::ServeListCasts()
{
  QString sql;
  RDSqlQuery *q;

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  cast_cast_id=-1;

  GetUserPerms();

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Podcasts</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  //
  // Title Section
  //
  TitleSection(QString().sprintf("%s Podcasts",(const char *)cast_key_name),
	       RDCASTMANAGER_COMMAND_LIST_CASTS,6);

  //
  // Column Headers
  //
  printf("<tr>\n");
  printf("<th>&nbsp;</th>\n");
  printf("<th>TITLE</th>\n");
  printf("<th>POSTED</th>\n");
  printf("<th>EXPIRES</th>\n");
  printf("<th>LENGTH</th>\n");
  printf("<th>CATEGORY</th>\n");
  printf("<th>&nbsp;</th>\n");
  printf("<th>&nbsp;</th>\n");
  printf("</tr>\n");

  //
  // Data Rows
  //
  QString line_colors[2];
  line_colors[0]=RD_WEB_LINE_COLOR1;
  line_colors[1]=RD_WEB_LINE_COLOR2;
  int current_color=0;
  sql=QString().sprintf("select ID,STATUS,ITEM_TITLE,ORIGIN_DATETIME,\
                         SHELF_LIFE,ITEM_CATEGORY,AUDIO_TIME from PODCASTS \
                         where FEED_ID=%d order by ORIGIN_DATETIME",
			cast_feed_id);
  q=new RDSqlQuery(sql);
  while(q->next()) {
    printf("<tr>\n");
    switch((RDPodcast::Status)q->value(1).toUInt()) {
      case RDPodcast::StatusActive:
	printf("<td bgcolor=\"%s\"><img src=\"greenball.png\" border=\"0\"></td>\n",(const char *)line_colors[current_color]);
	break;

      case RDPodcast::StatusPending:
	printf("<td bgcolor=\"%s\"><img src=\"redball.png\" border=\"0\"></td>\n",(const char *)line_colors[current_color]);
	break;

      case RDPodcast::StatusExpired:
	printf("<td bgcolor=\"%s\"><img src=\"whiteball.png\" border=\"0\"></td>\n",(const char *)line_colors[current_color]);
	break;
    }
    printf("<td bgcolor=\"%s\">%s</td>\n",
	   (const char *)line_colors[current_color],
	   (const char *)RDEscapeWebString(q->value(2).toString()));
    printf("<td aling=\"center\" bgcolor=\"%s\">%s</td>\n",
	   (const char *)line_colors[current_color],
	   (const char *)q->value(3).toDateTime().
	   toString("hh:mm:ss MM/dd/yyyy"));
    if(q->value(4).toInt()>0) {
      printf("<td align=\"center\" bgcolor=\"%s\">%s</td>\n",
	     (const char *)line_colors[current_color],
	     (const char *)q->value(3).toDateTime().
	     addDays(q->value(4).toInt()).toString("hh:mm:ss MM/dd/yyyy"));
    }
    else {
      printf("<td align=\"center\" bgcolor=\"%s\">Never</td>\n",
	     (const char *)line_colors[current_color]);
    }
    printf("<td align=\"center\" bgcolor=\"%s\">%s</td>\n",
	   (const char *)line_colors[current_color],
	   (const char *)RDGetTimeLength(q->value(6).toInt(),false,false));
    printf("<td align=\"center\" bgcolor=\"%s\">%s</td>\n",
	   (const char *)line_colors[current_color],
	   (const char *)RDEscapeWebString(q->value(5).toString()));

    //
    // Edit Button
    //
    if(cast_edit_priv) {
      printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
      printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">\n",
	   (const char *)line_colors[current_color]);
      SetContext(RDCASTMANAGER_COMMAND_EDIT_CAST);
      printf("<input type=\"hidden\" name=\"CAST_ID\" value=\"%d\">\n",
	     q->value(0).toInt());
      printf("<input type=\"submit\" value=\"Edit\">\n");
      printf("</td>\n");
      printf("</form>\n");
    }
    else {
      printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">\n",
	   (const char *)line_colors[current_color]);
      printf("&nbsp;\n");
      printf("</td>\n");
    }

    //
    // Delete Button
    //
    if(cast_delete_priv) {
      printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
      printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">\n",
	     (const char *)line_colors[current_color]);
      SetContext(RDCASTMANAGER_COMMAND_CONFIRM_DELETE_CAST);
      printf("<input type=\"hidden\" name=\"CAST_ID\" value=\"%d\">\n",
	     q->value(0).toInt());
      printf("<input type=\"submit\" value=\"Delete\">\n");
      printf("</td>\n");
      printf("</form>\n");
    }
    else {
      printf("<td align=\"center\" valign=\"middle\" bgcolor=\"%s\">\n",
	   (const char *)line_colors[current_color]);
      printf("&nbsp;\n");
      printf("</td>\n");
    }
    printf("</tr>\n");
    if(current_color==0) {
      current_color=1;
    }
    else {
      current_color=0;
    }
  }
  delete q;

  //
  // Subscription Report Button
  //
  printf("<tr><td colspan=\"5\">&nbsp;</td></tr>\n");
  printf("<tr>\n");
  printf("<td>&nbsp;</td>\n");

  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"center\" valign=\"middle\">\n");
  SetContext(RDCASTMANAGER_COMMAND_SUBSCRIPTION_PICK_DATES);
  printf("<input type=\"submit\" value=\"Subscription Report\">\n");
  printf("</td>\n");
  printf("</form>\n");

  //
  // Close Button
  //
  printf("<td colspan=\"5\">&nbsp;</td>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"center\" valign=\"middle\">\n");
  printf("<input type=\"hidden\" name=\"SESSION_ID\" value=\"%ld\">\n",
	 cast_session_id);
  printf("<input type=\"hidden\" name=\"COMMAND\" value=\"%d\">\n",
	 RDCASTMANAGER_COMMAND_LIST_FEEDS);
  printf("<input type=\"submit\" value=\"Close\">\n");
  printf("</td>\n");
  printf("</form>\n");
  printf("</tr>\n");

  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::ServeEditCast()
{
  QString sql;
  RDSqlQuery *q;

  GetUserPerms();
  if(!cast_edit_priv) {
    RDCgiError("Insufficient privileges for this operation!");
    exit(0);
  }

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  if(cast_cast_id<0) {
    RDCgiError("Missing CAST_ID");
    exit(0);
  }

  sql=QString().sprintf("select ITEM_TITLE,ITEM_AUTHOR,\
                         ITEM_CATEGORY,ITEM_LINK,ITEM_DESCRIPTION,\
                         ITEM_COMMENTS,ITEM_SOURCE_TEXT,ITEM_SOURCE_URL,\
                         ITEM_COMMENTS,SHELF_LIFE,ORIGIN_DATETIME,STATUS \
                         from PODCASTS where ID=%d",cast_cast_id);
  q=new RDSqlQuery(sql);
  if(!q->first()) {
    delete q;
    RDCgiError("Unable to fetch cast record!");
    exit(0);
  }

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Editing \"%s\"</title>\n",
	 (const char *)q->value(0).toString());
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  //
  // Title Section
  //
  TitleSection("Editing Podcast",RDCASTMANAGER_COMMAND_EDIT_CAST,1);

  //
  // Cast Data
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  SetContext(RDCASTMANAGER_COMMAND_COMMIT_CAST);

  //
  // Title
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Title:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_TITLE\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",(const char *)RDEscapeWebString(q->value(0).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Author E-Mail
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Author E-Mail:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_AUTHOR\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",(const char *)RDEscapeWebString(q->value(1).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Category
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Category:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_CATEGORY\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",(const char *)RDEscapeWebString(q->value(2).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Link URL
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Link URL:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_LINK\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",(const char *)RDEscapeWebString(q->value(3).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Description
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\" valign=\"top\">Description:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<textarea name=\"ITEM_DESCRIPTION\" rows=\"4\" cols=\"50\">%s</textarea>\n",(const char *)RDEscapeWebString(q->value(4).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Source Text
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Source Text:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_SOURCE_TEXT\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",
	 (const char *)RDEscapeWebString(q->value(6).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Source Link
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Source Link:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_SOURCE_URL\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",
	 (const char *)RDEscapeWebString(q->value(7).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Comments
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Comments:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ITEM_COMMENTS\" value=\"%s\" size=\"50\" maxlength=\"255\">\n",(const char *)RDEscapeWebString(q->value(5).toString()));
  printf("</td>\n");
  printf("</tr>\n");

  //
  // Origin Datetime
  //
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" align=\"right\">Posted At:</td>\n",
	 RD_WEB_LINE_COLOR1);
  printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	 RD_WEB_LINE_COLOR1);
  printf("<input type=\"text\" name=\"ORIGIN_DATETIME\" value=\"%s\" size=\"25\" maxlength=\"255\" readonly>\n",(const char *)q->value(10).toDateTime().
	 toString("MM/dd/yyyy - hh:mm:ss"));
  printf("</td>\n");
  printf("</tr>\n");

  QDateTime exp=q->value(10).toDateTime().addDays(q->value(9).toInt());
  if(q->value(11).toInt()==RDPodcast::StatusExpired) {
    printf("<input type=\"hidden\" name=\"STATUS\" value=\"%u\">\n",
	   RDPodcast::StatusExpired);
    if(q->value(9).toInt()==0) {
      printf("<input type=\"hidden\" name=\"EXPIRES\" value=\"No\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_MONTH\" value=\"0\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_DAY\" value=\"0\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_YEAR\" value=\"0\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_HOUR\" value=\"0\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_MINUTE\" value=\"0\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_SECOND\" value=\"0\">\n");
    }
    else {
      printf("<input type=\"hidden\" name=\"EXPIRES\" value=\"Yes\">\n");
      printf("<input type=\"hidden\" name=\"EXPIRATION_MONTH\" value=\"%s\">\n",(const char *)exp.toString("MM"));
      printf("<input type=\"hidden\" name=\"EXPIRATION_DAY\" value=\"%s\">\n",
	     (const char *)exp.toString("dd"));
      printf("<input type=\"hidden\" name=\"EXPIRATION_YEAR\" value=\"%s\">\n",
	     (const char *)exp.toString("yyyy"));
      printf("<input type=\"hidden\" name=\"EXPIRATION_HOUR\" value=\"%s\">\n",
	     (const char *)exp.toString("hh"));
      printf("<input type=\"hidden\" name=\"EXPIRATION_MINUTE\" value=\"%s\">\n",(const char *)exp.toString("mm"));
      printf("<input type=\"hidden\" name=\"EXPIRATION_SECOND\" value=\"%s\">\n",(const char *)exp.toString("ss"));
    }
  }
  else {
    //
    // Expiration
    //
    printf("<tr>\n");
    printf("<td bgcolor=\"%s\" align=\"right\">Cast Expires:</td>\n",
	   RD_WEB_LINE_COLOR1);
    printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	   RD_WEB_LINE_COLOR1);
    printf("<select name=\"EXPIRES\">\n");
    if(q->value(9).toInt()==0) {
      printf("<option value=\"yes\">Yes</option>\n");
      printf("<option value=\"no\" selected>No</option>\n");
    }
    else {
      printf("<option value=\"yes\" selected>Yes</option>\n");
      printf("<option value=\"no\">No</option>\n");
    }
    printf("</select>\n");
    printf("</td>\n");
    printf("</tr>\n");
    
    if(q->value(9).toInt()==0) {
      printf("<tr>\n");
      printf("<td bgcolor=\"%s\" align=\"right\">Expires At:</td>\n",
	     RD_WEB_LINE_COLOR1);
      printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	     RD_WEB_LINE_COLOR1);
      printf("<input type=\"text\" name=\"EXPIRATION_MONTH\" value=\"0\" size=\"2\" maxlength=\"2\">/\n");
      printf("<input type=\"text\" name=\"EXPIRATION_DAY\" value=\"0\" size=\"2\" maxlength=\"2\">/\n");
      printf("<input type=\"text\" name=\"EXPIRATION_YEAR\" value=\"0\" size=\"4\" maxlength=\"4\"> -- \n");
      printf("<input type=\"text\" name=\"EXPIRATION_HOUR\" value=\"0\" size=\"2\" maxlength=\"2\">:\n");
      printf("<input type=\"text\" name=\"EXPIRATION_MINUTE\" value=\"0\" size=\"2\" maxlength=\"2\">:\n");
      printf("<input type=\"text\" name=\"EXPIRATION_SECOND\" value=\"0\" size=\"2\" maxlength=\"2\">\n");
      printf("</td>\n");
      printf("</tr>\n");
    }
    else {
      printf("<tr>\n");
      printf("<td bgcolor=\"%s\" align=\"right\">Expires At:</td>\n",
	     RD_WEB_LINE_COLOR1);
      printf("<td bgcolor=\"%s\" align=\"left\" colspan=\"2\">\n",
	     RD_WEB_LINE_COLOR1);
      printf("<input type=\"text\" name=\"EXPIRATION_MONTH\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)exp.toString("MM"));
      printf("<input type=\"text\" name=\"EXPIRATION_DAY\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)exp.toString("dd"));
      printf("<input type=\"text\" name=\"EXPIRATION_YEAR\" value=\"%s\" size=\"4\" maxlength=\"4\"> -- \n",(const char *)exp.toString("yyyy"));
      printf("<input type=\"text\" name=\"EXPIRATION_HOUR\" value=\"%s\" size=\"2\" maxlength=\"2\">:\n",(const char *)exp.toString("hh"));
      printf("<input type=\"text\" name=\"EXPIRATION_MINUTE\" value=\"%s\" size=\"2\" maxlength=\"2\">:\n",(const char *)exp.toString("mm"));
      printf("<input type=\"text\" name=\"EXPIRATION_SECOND\" value=\"%s\" size=\"2\" maxlength=\"2\">\n",(const char *)exp.toString("ss"));
      printf("</td>\n");
      printf("</tr>\n");
    }
    
    //
    // Posting Status
    //
    printf("<tr>\n");
    printf("<td bgcolor=\"%s\" align=\"right\">Posting Status:</td>\n",
	   RD_WEB_LINE_COLOR1);
    printf("<td bgcolor=\"%s\" align=\"left\">\n",RD_WEB_LINE_COLOR1);
    if(q->value(11).toInt()==RDPodcast::StatusPending) {
      printf("<input type=\"radio\" name=\"STATUS\" value=%d checked=\"checked\">Hold\n",
	     RDPodcast::StatusPending);
      printf("</td>\n");
      printf("<td bgcolor=\"%s\" align=\"left\">\n",RD_WEB_LINE_COLOR1);
      printf("<input type=\"radio\" name=\"STATUS\" value=%d>Active</td>\n",
	     RDPodcast::StatusActive);
    }
    else {
      printf("<input type=\"radio\" name=\"STATUS\" value=%d>Hold\n",
	     RDPodcast::StatusPending);
      printf("</td>\n");
      printf("<td bgcolor=\"%s\" align=\"left\">\n",RD_WEB_LINE_COLOR1);
      printf("<input type=\"radio\" name=\"STATUS\" value=%d checked=\"checked\">Active</td>\n",
	     RDPodcast::StatusActive);
      
    }
    printf("</td>\n");
    printf("</tr>\n");
  }

  printf("<tr>\n");

  //
  // OK Button
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"right\" valign=\"middle\">\n");
  printf("<input type=\"submit\" value=\"OK\">\n");
  printf("</td>\n");
  printf("</form>\n");

  //
  // Cancel Button
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"left\" valign=\"left\">\n");
  int id=cast_cast_id;   // UGLY, UGLY UGLY!!!
  cast_cast_id=-1;
  SetContext(RDCASTMANAGER_COMMAND_LIST_CASTS);
  cast_cast_id=id;
  printf("<input type=\"submit\" value=\"Cancel\">\n");
  printf("</td>\n");
  printf("</form>\n");

  //
  // Episode Report Button
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"right\" valign=\"right\">\n");
  SetContext(RDCASTMANAGER_COMMAND_EPISODE_PICK_DATES);
  printf("<input type=\"submit\" value=\"Episode Report\">\n");
  printf("</td>\n");
  printf("</form>\n");

  printf("</tr>\n");



  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::CommitCast()
{
  QString sql;
  RDSqlQuery *q;
  char str[1024];
  QString item_title;
  QString item_author;
  QString item_category;
  QString item_link;
  QString item_description;
  QString item_source_text;
  QString item_source_url;
  QString item_comments;
  QString expires;
  int expiration_day=0;
  int expiration_month=0;
  int expiration_year=0;
  int expiration_hour=0;
  int expiration_minute=0;
  int expiration_second=0;
  unsigned shelf_life=0;
  int status;

  GetUserPerms();
  if(!cast_edit_priv) {
    RDCgiError("Insufficient privileges for this operation!");
    exit(0);
  }

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  if(cast_cast_id<0) {
    RDCgiError("Missing CAST_ID");
    exit(0);
  }
  if((RDGetPostString(cast_post,"ITEM_TITLE",str,1024))<0) {
    RDCgiError("Missing ITEM_TITLE");
    exit(0);
  }
  item_title=str;
  if((RDGetPostString(cast_post,"ITEM_AUTHOR",str,1024))<0) {
    RDCgiError("Missing ITEM_AUTHOR");
    exit(0);
  }
  item_author=str;
  if((RDGetPostString(cast_post,"ITEM_CATEGORY",str,1024))<0) {
    RDCgiError("Missing ITEM_CAEGORY");
    exit(0);
  }
  item_category=str;
  if((RDGetPostString(cast_post,"ITEM_LINK",str,1024))<0) {
    RDCgiError("Missing ITEM_LINK");
    exit(0);
  }
  item_link=str;
  if((RDGetPostString(cast_post,"ITEM_DESCRIPTION",str,1024))<0) {
    RDCgiError("Missing ITEM_DESCRIPTION");
    exit(0);
  }
  item_description=str;
  if((RDGetPostString(cast_post,"ITEM_SOURCE_TEXT",str,1024))<0) {
    RDCgiError("Missing ITEM_SOURCE_TEXT");
    exit(0);
  }
  item_source_text=str;
  if((RDGetPostString(cast_post,"ITEM_SOURCE_URL",str,1024))<0) {
    RDCgiError("Missing ITEM_SOURCE_URL");
    exit(0);
  }
  item_source_url=str;
  if((RDGetPostString(cast_post,"ITEM_COMMENTS",str,1024))<0) {
    RDCgiError("Missing ITEM_COMMENTS");
    exit(0);
  }
  item_comments=str;
  if((RDGetPostString(cast_post,"EXPIRES",str,1024))<0) {
    RDCgiError("Missing EXPIRES");
    exit(0);
  }
  expires=str;
  if(expires.lower()=="yes") {
    if((RDGetPostInt(cast_post,"EXPIRATION_DAY",&expiration_day))<0) {
      RDCgiError("Missing EXPIRATION_DAY");
      exit(0);
    }
    if((RDGetPostInt(cast_post,"EXPIRATION_MONTH",&expiration_month))<0) {
      RDCgiError("Missing EXPIRATION_MONTH");
      exit(0);
    }
    if((RDGetPostInt(cast_post,"EXPIRATION_YEAR",&expiration_year))<0) {
      RDCgiError("Missing EXPIRATION_YEAR");
      exit(0);
    }
    if((RDGetPostInt(cast_post,"EXPIRATION_HOUR",&expiration_hour))<0) {
      RDCgiError("Missing EXPIRATION_HOUR");
      exit(0);
    }
    if((RDGetPostInt(cast_post,"EXPIRATION_MINUTE",&expiration_minute))<0) {
      RDCgiError("Missing EXPIRATION_MINUTE");
      exit(0);
    }
    if((RDGetPostInt(cast_post,"EXPIRATION_SECOND",&expiration_second))<0) {
      RDCgiError("Missing EXPIRATION_SECOND");
      exit(0);
    }
    sql=QString().sprintf("select ORIGIN_DATETIME from PODCASTS \
                           where ID=%d",cast_cast_id);
    q=new RDSqlQuery(sql);
    if(!q->first()) {
      delete q;
      RDCgiError("Unable to access cast record!");
    }
    QDateTime dt(QDate(expiration_year,expiration_month,expiration_day),
		 QTime(expiration_hour,expiration_minute,expiration_second));
    if(dt.isNull()) {
      RDCgiError("Invalid expiration date!");
      delete q;
      exit(0);
    }
    shelf_life=q->value(0).toDateTime().daysTo(dt);
    delete q;
  }
  if((RDGetPostInt(cast_post,"STATUS",&status))<0) {
    RDCgiError("Missing STATUS");
    exit(0);
  }

  sql=QString().sprintf("update PODCASTS set \
                         STATUS=%d,\
                         ITEM_TITLE=\"%s\",\
                         ITEM_DESCRIPTION=\"%s\",\
                         ITEM_CATEGORY=\"%s\",\
                         ITEM_LINK=\"%s\",\
                         ITEM_COMMENTS=\"%s\",\
                         ITEM_AUTHOR=\"%s\",\
                         ITEM_SOURCE_TEXT=\"%s\",\
                         ITEM_SOURCE_URL=\"%s\",\
                         SHELF_LIFE=%d \
                         where ID=%d",
			status,
			(const char *)RDEscapeString(item_title),
			(const char *)RDEscapeString(item_description),
			(const char *)RDEscapeString(item_category),
			(const char *)RDEscapeString(item_link),
			(const char *)RDEscapeString(item_comments),
			(const char *)RDEscapeString(item_author),
			(const char *)RDEscapeString(item_source_text),
			(const char *)RDEscapeString(item_source_url),
			shelf_life,
			cast_cast_id);
  q=new RDSqlQuery(sql);
  delete q;

  ServeListCasts();
}


void MainObject::ConfirmDeleteCast()
{
  QString sql;
  RDSqlQuery *q;

  GetUserPerms();
  if(!cast_delete_priv) {
    RDCgiError("Insufficient privileges for this operation!");
    exit(0);
  }

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  if(cast_cast_id<0) {
    RDCgiError("Missing CAST_ID");
    exit(0);
  }

  sql=QString().sprintf("select ITEM_TITLE,ORIGIN_DATETIME from PODCASTS \
                         where ID=%d",cast_cast_id);
  q=new RDSqlQuery(sql);
  if(!q->first()) {
    RDCgiError("unable to access cast record!");
    exit(0);
  }
  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Confirm Delete</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");
  printf("<tr>\n");
  printf("<td bgcolor=\"%s\" >Are you sure you want to delete cast \"%s\", posted on %s at %s?</td>\n",
	 RD_WEB_LINE_COLOR2,
	 (const char *)q->value(0).toString(),
	 (const char *)q->value(1).toDateTime().toString("MM/dd/yyyy"),
	 (const char *)q->value(1).toDateTime().toString("hh:mm:ss"));
  printf("</tr>\n");

  printf("<tr>\n");
  printf("<tr><td>&nbsp;</td></tr>\n");

  printf("<tr><td align=\"right\">\n");
  printf("<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n");
  printf("<tr>\n");
  
  //
  // Yes Button
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td>\n");
  SetContext(RDCASTMANAGER_COMMAND_DELETE_CAST);
  printf("<input type=\"submit\" value=\"Yes\">\n");
  printf("</td>\n");
  printf("</form>\n");

  printf("<td>&nbsp;</td>\n");

  //
  // No Button
  //
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td>\n");
  cast_cast_id=-1;
  SetContext(RDCASTMANAGER_COMMAND_LIST_CASTS);
  printf("<input type=\"submit\" value=\"No\">\n");
  printf("</td>\n");
  printf("</form>\n");

  printf("</tr>\n");
  printf("</table>\n");

  printf("</td>\n");
  printf("</tr>\n");
  printf("</table>\n");
}


void MainObject::DeleteCast()
{
  QString sql;
  RDSqlQuery *q;

  if(!cast_delete_priv) {
    RDCgiError("Insufficient privileges for this operation!");
    exit(0);
  }

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  if(cast_cast_id<0) {
    RDCgiError("Missing CAST_ID");
    exit(0);
  }

  RDPodcast *cast=new RDPodcast(cast_cast_id);
  system(cast->audioPurgeCommand());
  delete cast;

  sql=QString().sprintf("delete from PODCASTS where ID=%d",cast_cast_id);
  q=new RDSqlQuery(sql);
  delete q;

  ServeListCasts();
}


void MainObject::ServeSubscriptionReport()
{
  QString sql;
  RDSqlQuery *q;
  unsigned rss_total=0;
  unsigned audio_total=0;
  unsigned total=0;

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  cast_cast_id=-1;
  RDFeed *feed=new RDFeed(cast_key_name);

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Subscription Report</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  //
  // Title Section
  //
  printf("<tr>\n");
  printf("<td align=\"center\" colspan=\"2\">\n");
  printf("<big><big><strong>Subscription Report for \"%s\"</strong></big></big>\n",
	 (const char *)feed->channelTitle());
  printf("</td></tr>\n");

  //
  // Column Headers
  //
  printf("<tr>\n");
  printf("<th>DATE</th>\n");
  printf("<th align=\"right\">RSS</th>\n");
  printf("<th align=\"right\">AUDIO</th>\n");
  printf("</tr>\n");


  //
  // Data Rows
  //
  QString line_colors[2];
  line_colors[0]=RD_WEB_LINE_COLOR1;
  line_colors[1]=RD_WEB_LINE_COLOR2;
  int current_color=0;
  QString keyname_esc=cast_key_name;
  keyname_esc.replace(" ","_");
  sql=QString().sprintf("select ACCESS_DATE,ACCESS_COUNT,CAST_ID from %s_FLG \
                         where (ACCESS_DATE>=\"%s\")&&\
                         (ACCESS_DATE<=\"%s\") \
                         order by ACCESS_DATE,CAST_ID desc",
			(const char *)keyname_esc,
			(const char *)cast_start_date.toString("yyyy-MM-dd"),
			(const char *)cast_end_date.toString("yyyy-MM-dd"));
  q=new RDSqlQuery(sql);
  while(q->next()) {
    if(q->value(2).toUInt()==0) {
      printf("<tr><td align=\"center\" bgcolor=\"%s\">%s</td>\n",
	     (const char *)line_colors[current_color],
	     (const char *)q->value(0).toDate().
	     toString("MM/dd/yyyy"));
      printf("<td align=\"right\" bgcolor=\"%s\">%9u</td>\n",
	     (const char *)line_colors[current_color],
	     q->value(1).toUInt());
      printf("<td align=\"right\" bgcolor=\"%s\">%9u</td></tr>\n",
	     (const char *)line_colors[current_color],
	     total);
      total=0;
      rss_total+=q->value(1).toUInt();
      if(current_color==0) {
	current_color=1;
      }
      else {
	current_color=0;
      }
    }
    else {
      total+=q->value(1).toUInt();
      audio_total+=q->value(1).toUInt();
    }
  }
  printf("<tr><td align=\"center\"><strong>TOTAL</strong></td>\n");
  printf("<td align=\"right\"><strong>%9u</strong></td>\n",rss_total);
  printf("<td align=\"right\"><strong>%9u</strong></td></tr>\n",audio_total);
  delete q;

  delete feed;

  //
  // Close Button
  //
  printf("<tr><td colspan=\"3\">&nbsp;</td></tr>\n");
  printf("<tr>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td>&nbsp;</td>\n");
  printf("<td>&nbsp;</td>\n");
  printf("<td align=\"right\">\n");
  SetContext(RDCASTMANAGER_COMMAND_LIST_CASTS);
  printf("<input type=\"submit\" value=\"Close\">\n");
  printf("</td>\n");
  printf("</form>\n");
  printf("</tr>\n");

  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::ServeEpisodeReport()
{
  QString sql;
  RDSqlQuery *q;
  unsigned total=0;

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }
  if(cast_cast_id<0) {
    RDCgiError("Missing CAST_ID");
    exit(0);
  }
  RDPodcast *cast=new RDPodcast(cast_cast_id);

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Episode Report</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  //
  // Title Section
  //
  printf("<tr>\n");
  printf("<td align=\"center\" colspan=\"2\">\n");
  printf("<big><big><strong>Episode Report for \"%s\"</strong></big></big>\n",
	 (const char *)cast->itemTitle());
  printf("</td></tr>\n");

  //
  // Column Headers
  //
  printf("<tr>\n");
  printf("<th>DATE</th>\n");
  printf("<th align=\"right\">DOWNLOADS</th>\n");
  printf("</tr>\n");


  //
  // Data Rows
  //
  QString line_colors[2];
  line_colors[0]=RD_WEB_LINE_COLOR1;
  line_colors[1]=RD_WEB_LINE_COLOR2;
  int current_color=0;
  QString keyname_esc=cast_key_name;
  keyname_esc.replace(" ","_");
  sql=QString().sprintf("select ACCESS_DATE,ACCESS_COUNT from %s_FLG \
                         where (ACCESS_DATE>=\"%s\")&&\
                         (ACCESS_DATE<=\"%s\")&& \
                         (CAST_ID=%d) order by ACCESS_DATE",
			(const char *)keyname_esc,
			(const char *)cast_start_date.toString("yyyy-MM-dd"),
			(const char *)cast_end_date.toString("yyyy-MM-dd"),
			cast_cast_id);
  q=new RDSqlQuery(sql);
  while(q->next()) {
    printf("<tr><td align=\"center\" bgcolor=\"%s\">%s</td>\n",
	   (const char *)line_colors[current_color],
	   (const char *)q->value(0).toDate().
	   toString("MM/dd/yyyy"));
    printf("<td align=\"right\" bgcolor=\"%s\">%9u</td></tr>\n",
	   (const char *)line_colors[current_color],
	   q->value(1).toUInt());
    if(current_color==0) {
      current_color=1;
    }
    else {
      current_color=0;
    }
    total+=q->value(1).toUInt();
  }
  printf("<tr><td align=\"center\"><strong>TOTAL</strong></td>\n");
  printf("<td align=\"right\"><strong>%9u</strong></td></tr>\n",total);
  delete q;

  delete cast;

  //
  // Close Button
  //
  printf("<tr><td colspan=\"2\">&nbsp;</td></tr>\n");
  printf("<tr>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td>&nbsp;</td>\n");
  printf("<td align=\"right\">\n");
  SetContext(RDCASTMANAGER_COMMAND_EDIT_CAST);
  printf("<input type=\"submit\" value=\"Close\">\n");
  printf("</td>\n");
  printf("</form>\n");
  printf("</tr>\n");

  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::ServeDatePicker(int cmd)
{
  QDate yesterday_date=QDate::currentDate().addDays(-1);

  GetContext();
  if(cast_key_name.isEmpty()) {
    RDCgiError("Missing KEY_NAME");
    exit(0);
  }
  if(cast_feed_id<0) {
    RDCgiError("Missing FEED_ID");
    exit(0);
  }

  printf("Content-type: text/html\n\n");
  printf("<html>\n");
  printf("<head>\n");
  printf("<title>Rivendell RDCastManager -- Choose Dates</title>\n");
  printf("</head>\n");

  printf("<body bgcolor=\"%s\">\n",RD_WEB_BACKGROUND_COLOR);
  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  printf("<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\">\n");

  printf("<tr>\n");
  printf("<td align=\"center\" colspan=\"2\">SELECT DATES</td>\n");
  printf("</tr>\n");

  //
  // Start Date
  //
  printf("<tr>\n");
  printf("<td align=\"right\">Start Date:</td>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  SetContext(cmd);
  printf("<td><input type=\"text\" name=\"START_MONTH\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)yesterday_date.addMonths(-1).toString("MM"));
  printf("<input type=\"text\" name=\"START_DAY\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)yesterday_date.addMonths(-1).toString("dd"));
  printf("<input type=\"text\" name=\"START_YEAR\" value=\"%s\" size=\"4\" maxlength=\"4\"></td>\n",(const char *)yesterday_date.addMonths(-1).toString("yyyy"));
  printf("</tr>\n");

  //
  // End Date
  //
  printf("<tr>\n");
  printf("<td align=\"right\">End Date:</td>\n");
  printf("<td><input type=\"text\" name=\"END_MONTH\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)yesterday_date.toString("MM"));
  printf("<input type=\"text\" name=\"END_DAY\" value=\"%s\" size=\"2\" maxlength=\"2\">/\n",(const char *)yesterday_date.toString("dd"));
  printf("<input type=\"text\" name=\"END_YEAR\" value=\"%s\" size=\"4\" maxlength=\"4\"></td>\n",(const char *)yesterday_date.toString("yyyy"));
  printf("</tr>\n");

  //
  // Next Button
  //
  printf("<tr>\n");
  printf("<td align=\"right\" colspan=\"2\">\n");
  printf("<input type=\"submit\" value=\"Next\"></td>\n");
  printf("</form>\n");
  printf("</tr>\n");

  printf("</table>\n");
  printf("</body>\n");
  printf("</html>\n");
  exit(0);
}


void MainObject::TitleSection(const QString &title,int cmd,int colspan) const
{
  printf("<tr>\n");
  printf("<td>&nbsp;</td>\n");
  printf("<td align=\"center\" colspan=\"%d\"><big><big>%s</big></big></td>\n",
	 colspan,(const char *)title);
  printf("<td><table cellpadding=\"3\" cellspacing=\"3\" border=\"0\">\n");
  //
  // Refresh Button
  //
  printf("<tr>\n");
  printf("<form action=\"rdcastmanager.cgi\" method=\"post\">\n");
  printf("<td align=\"right\">\n");
  SetContext(cmd);
  printf("<input type=\"submit\" value=\"Refresh\">\n");
  printf("</td>\n");
  printf("</form>\n");
  printf("</tr>\n");
  printf("</table></td>\n");
  printf("</tr>\n");
}


void MainObject::GetUserPerms()
{
  QString sql;
  RDSqlQuery *q;

  sql=QString().sprintf("select USERS.ADD_PODCAST_PRIV,\
                         USERS.EDIT_PODCAST_PRIV,USERS.DELETE_PODCAST_PRIV \
                         from USERS left join WEB_CONNECTIONS \
                         on USERS.LOGIN_NAME=WEB_CONNECTIONS.LOGIN_NAME \
                         where WEB_CONNECTIONS.SESSION_ID=%ld",
			cast_session_id);
  q=new RDSqlQuery(sql);
  if(!q->first()) {
    delete q;
    RDCgiError("Unable to load user security settings!");
    exit(0);
  }
  cast_add_priv=RDBool(q->value(0).toString());
  cast_edit_priv=RDBool(q->value(1).toString());
  cast_delete_priv=RDBool(q->value(2).toString());
  delete q;
}


void MainObject::GetContext()
{
  int day=0;
  int month=0;
  int year=0;
  char str[1024];

  //
  // Basic Context
  //
  RDGetPostString(cast_post,"KEY_NAME",str,1024);
  cast_key_name=str;
  RDGetPostInt(cast_post,"FEED_ID",&cast_feed_id);
  RDGetPostInt(cast_post,"CAST_ID",&cast_cast_id);

  //
  // Start Date
  //
  RDGetPostInt(cast_post,"START_DAY",&day);
  RDGetPostInt(cast_post,"START_MONTH",&month);
  RDGetPostInt(cast_post,"START_YEAR",&year);
  cast_start_date=QDate(year,month,day);

  //
  // End Date
  //
  day=0;
  month=0;
  year=0;
  RDGetPostInt(cast_post,"END_DAY",&day);
  RDGetPostInt(cast_post,"END_MONTH",&month);
  RDGetPostInt(cast_post,"END_YEAR",&year);
  cast_end_date=QDate(year,month,day);
}


void MainObject::SetContext(int cmd) const
{
  printf("<input type=\"hidden\" name=\"COMMAND\" value=\"%d\">\n",
	 cmd);
  printf("<input type=\"hidden\" name=\"SESSION_ID\" value=\"%ld\">\n",
	 cast_session_id);
  if(!cast_key_name.isEmpty()) {
    printf("<input type=\"hidden\" name=\"KEY_NAME\" value=\"%s\">\n",
	   (const char *)cast_key_name);
  }
  if(cast_feed_id>=0) {
    printf("<input type=\"hidden\" name=\"FEED_ID\" value=\"%d\">\n",
	   cast_feed_id);
  }
  if(cast_cast_id>=0) {
    printf("<input type=\"hidden\" name=\"CAST_ID\" value=\"%d\">\n",
	   cast_cast_id);
  }
}


int main(int argc,char *argv[])
{
  QApplication a(argc,argv,false);
  new MainObject(NULL,"main");
  return a.exec();
}
