mirror of
				https://github.com/ElvishArtisan/rivendell.git
				synced 2025-11-03 23:53:59 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			380 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			380 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// rdfeed_script.cpp
 | 
						|
//
 | 
						|
// An RSS Feed Generator for Rivendell.
 | 
						|
//
 | 
						|
//   (C) Copyright 2002-2007,2016-2018 Fred Gleason <fredg@paravelsystems.com>
 | 
						|
//
 | 
						|
//   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 <map>
 | 
						|
 | 
						|
#include <qapplication.h>
 | 
						|
#include <qdatetime.h>
 | 
						|
#include <qstringlist.h>
 | 
						|
 | 
						|
#include <rdapplication.h>
 | 
						|
#include <rdconf.h>
 | 
						|
#include <rddb.h>
 | 
						|
#include <rdescape_string.h>
 | 
						|
#include <rdfeed.h>
 | 
						|
#include <rdfeedlog.h>
 | 
						|
#include <rdformpost.h>
 | 
						|
#include <rdpodcast.h>
 | 
						|
#include <dbversion.h>
 | 
						|
#include <rdweb.h>
 | 
						|
 | 
						|
#include "rdfeed_script.h"
 | 
						|
 | 
						|
char server_name[PATH_MAX];
 | 
						|
 | 
						|
MainObject::MainObject(QObject *parent)
 | 
						|
  :QObject(parent)
 | 
						|
{
 | 
						|
  QString err_msg;
 | 
						|
 | 
						|
  char keyname[10];
 | 
						|
  int cast_id=-1;
 | 
						|
  bool count;
 | 
						|
 | 
						|
  //
 | 
						|
  // Validate Feed Key Name
 | 
						|
  //
 | 
						|
  if(getenv("QUERY_STRING")==NULL) {
 | 
						|
    printf("Content-type: text/html\n");
 | 
						|
    printf("Status: 400\n");
 | 
						|
    printf("\n");
 | 
						|
    printf("rdfeed: missing feed key name\n");
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
  int arg=0;
 | 
						|
  while((getenv("QUERY_STRING")[arg]!=0)&&
 | 
						|
	(getenv("QUERY_STRING")[arg]!='&')&&(arg<9)) {
 | 
						|
    keyname[arg]=getenv("QUERY_STRING")[arg];
 | 
						|
    arg++;
 | 
						|
  }
 | 
						|
  if(arg==9) {
 | 
						|
    printf("Content-type: text/html\n");
 | 
						|
    printf("Status: 400\n");
 | 
						|
    printf("\n");
 | 
						|
    printf("rdfeed: invalid feed key name\n");
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
  keyname[arg]=0;
 | 
						|
  RDGetPostInt(getenv("QUERY_STRING")+arg+1,"cast_id",&cast_id);
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the Server Name
 | 
						|
  //
 | 
						|
  if(getenv("SERVER_NAME")==NULL) {
 | 
						|
    printf("Content-type: text/html\n");
 | 
						|
    printf("Status: 500\n");
 | 
						|
    printf("\n");
 | 
						|
    printf("rdfeed: missing SERVER_NAME\n");
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
  strncpy(server_name,getenv("SERVER_NAME"),PATH_MAX);
 | 
						|
 | 
						|
  //
 | 
						|
  // Determine Range
 | 
						|
  //
 | 
						|
  if(getenv("HTTP_RANGE")!=NULL) {
 | 
						|
    count=ShouldCount(getenv("HTTP_RANGE"));
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    count=true;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Open the Database
 | 
						|
  //
 | 
						|
  rda=new RDApplication("rdfeed.xml","rdfeed.xml",RDFEED_XML_USAGE,this);
 | 
						|
  if(!rda->open(&err_msg)) {
 | 
						|
    printf("Content-type: text/html\n");
 | 
						|
    printf("Status: 500\n");
 | 
						|
    printf("\n");
 | 
						|
    printf("rdfeed.xml: %s\n",(const char *)err_msg.utf8());
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
 | 
						|
  /*
 | 
						|
  printf("Content-type: text/html\n\n");
 | 
						|
  QString sql;
 | 
						|
  RDSqlQuery *q;
 | 
						|
  sql=QString("show variables like '%character_set%'");
 | 
						|
  q=new RDSqlQuery(sql);
 | 
						|
  while(q->next()) {
 | 
						|
    printf("%s: %s<br>\n",(const char *)q->value(0).toString(),
 | 
						|
	   (const char *)q->value(1).toString());
 | 
						|
  }
 | 
						|
  delete q;
 | 
						|
  sql=QString("show variables like '%collation%'");
 | 
						|
  q=new RDSqlQuery(sql);
 | 
						|
  while(q->next()) {
 | 
						|
    printf("%s: %s<br>\n",(const char *)q->value(0).toString(),
 | 
						|
	   (const char *)q->value(1).toString());
 | 
						|
  }
 | 
						|
  delete q;
 | 
						|
  exit(0);
 | 
						|
  */
 | 
						|
 | 
						|
  if(cast_id<0) {
 | 
						|
    ServeRss(keyname,count);
 | 
						|
  }
 | 
						|
  ServeLink(keyname,cast_id,count);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void MainObject::ServeRss(const char *keyname,bool count)
 | 
						|
{
 | 
						|
  QString sql;
 | 
						|
  RDSqlQuery *q;
 | 
						|
  RDSqlQuery *q1;
 | 
						|
 | 
						|
  sql=QString("select ")+
 | 
						|
    "CHANNEL_TITLE,"+        // 00
 | 
						|
    "CHANNEL_DESCRIPTION,"+  // 01
 | 
						|
    "CHANNEL_CATEGORY,"+     // 02
 | 
						|
    "CHANNEL_LINK,"+         // 03
 | 
						|
    "CHANNEL_COPYRIGHT,"+    // 04
 | 
						|
    "CHANNEL_WEBMASTER,"+    // 05
 | 
						|
    "CHANNEL_LANGUAGE,"+     // 06
 | 
						|
    "LAST_BUILD_DATETIME,"+  // 07
 | 
						|
    "ORIGIN_DATETIME,"+      // 08
 | 
						|
    "HEADER_XML,"+           // 09
 | 
						|
    "CHANNEL_XML,"+          // 10
 | 
						|
    "ITEM_XML,"+             // 11
 | 
						|
    "BASE_URL,"+             // 12
 | 
						|
    "ID,"+                   // 13
 | 
						|
    "UPLOAD_EXTENSION,"+     // 14
 | 
						|
    "CAST_ORDER,"+           // 15
 | 
						|
    "REDIRECT_PATH,"+        // 16
 | 
						|
    "BASE_PREAMBLE "+        // 17
 | 
						|
    "from FEEDS where "+
 | 
						|
    "KEY_NAME=\""+RDEscapeString(keyname)+"\"";
 | 
						|
  q=new RDSqlQuery(sql);
 | 
						|
  if(!q->first()) {
 | 
						|
    printf("Content-type: text/html\n\n");
 | 
						|
    printf("rdfeed: no feed matches the supplied key name\n");
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Log the Access
 | 
						|
  //
 | 
						|
  if(count) {
 | 
						|
    RDIncrementFeedCount(keyname);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Redirect if necessary
 | 
						|
  //
 | 
						|
  if(!q->value(16).toString().isEmpty()) {
 | 
						|
    Redirect(q->value(16).toString());
 | 
						|
    delete q;
 | 
						|
    exit(0);
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Generate CGI Header
 | 
						|
  //
 | 
						|
  printf("Content-type: application/rss+xml; charset=UTF-8\n\n");
 | 
						|
  
 | 
						|
  //
 | 
						|
  // Render Header XML
 | 
						|
  //
 | 
						|
  printf("%s\n",(const char *)q->value(9).toString().utf8());
 | 
						|
 | 
						|
  //
 | 
						|
  // Render Channel XML
 | 
						|
  //
 | 
						|
  printf("<channel>\n");
 | 
						|
  printf("%s\n",(const char *)ResolveChannelWildcards(q).utf8());
 | 
						|
 | 
						|
  //
 | 
						|
  // Render Item XML
 | 
						|
  //
 | 
						|
  sql=QString("select ")+
 | 
						|
    "ITEM_TITLE,"+          // 00
 | 
						|
    "ITEM_DESCRIPTION,"+    // 01
 | 
						|
    "ITEM_CATEGORY,"+       // 02
 | 
						|
    "ITEM_LINK,"+           // 03
 | 
						|
    "ITEM_AUTHOR,"+         // 04
 | 
						|
    "ITEM_SOURCE_TEXT,"+    // 05
 | 
						|
    "ITEM_SOURCE_URL,"+     // 06
 | 
						|
    "ITEM_COMMENTS,"+       // 07
 | 
						|
    "AUDIO_FILENAME,"+      // 08
 | 
						|
    "AUDIO_LENGTH,"+        // 09
 | 
						|
    "AUDIO_TIME,"+          // 10
 | 
						|
    "EFFECTIVE_DATETIME,"+  // 11
 | 
						|
    "ID "+                  // 12
 | 
						|
    "from PODCASTS where "+
 | 
						|
    QString().sprintf("(FEED_ID=%d)&&",q->value(13).toUInt())+
 | 
						|
    QString().sprintf("(STATUS=%d) ",RDPodcast::StatusActive)+
 | 
						|
    "order by ORIGIN_DATETIME";
 | 
						|
  if(q->value(15).toString()=="N") {
 | 
						|
    sql+=" desc";
 | 
						|
  }
 | 
						|
  q1=new RDSqlQuery(sql);
 | 
						|
  while(q1->next()) {
 | 
						|
    printf("<item>\n");
 | 
						|
    printf("%s\n",(const char *)ResolveItemWildcards(keyname,q1,q).utf8());
 | 
						|
    printf("</item>\n");
 | 
						|
  }
 | 
						|
  delete q1;
 | 
						|
 | 
						|
  printf("</channel>\n");
 | 
						|
  printf("</rss>\n");
 | 
						|
  delete q;
 | 
						|
 | 
						|
  exit(0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void MainObject::ServeLink(const char *keyname,int cast_id,bool count)
 | 
						|
{
 | 
						|
  QString sql;
 | 
						|
  RDSqlQuery *q;
 | 
						|
 | 
						|
  sql=QString("select ")+
 | 
						|
    "FEEDS.BASE_URL,"+           // 00
 | 
						|
    "PODCASTS.AUDIO_FILENAME "+  // 01
 | 
						|
    "from FEEDS left join PODCASTS "+
 | 
						|
    "on FEEDS.ID=PODCASTS.FEED_ID where "+
 | 
						|
    "(FEEDS.KEY_NAME=\""+RDEscapeString(keyname)+"\")&&"+
 | 
						|
    QString().sprintf("(PODCASTS.ID=%d)",cast_id);
 | 
						|
  q=new RDSqlQuery(sql);
 | 
						|
  if(!q->first()) {
 | 
						|
    delete q;
 | 
						|
    RDCgiError("Unable to retrieve cast record!");
 | 
						|
  }
 | 
						|
  if(count) {
 | 
						|
    RDIncrementCastCount(keyname,cast_id);
 | 
						|
  }
 | 
						|
  printf("Content-type: audio/x-mpeg\n");
 | 
						|
  printf("Location: %s/%s\n\n",(const char *)q->value(0).toString(),
 | 
						|
	 (const char *)q->value(1).toString().utf8());
 | 
						|
  delete q;
 | 
						|
 | 
						|
  exit(0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
QString MainObject::ResolveChannelWildcards(RDSqlQuery *chan_q)
 | 
						|
{
 | 
						|
  QString ret=chan_q->value(10).toString();
 | 
						|
  //  ret.replace("%TITLE%",chan_q->value(0).toString());
 | 
						|
  ret.replace("%TITLE%",RDXmlEscape(chan_q->value(0).toString()));
 | 
						|
  ret.replace("%DESCRIPTION%",RDXmlEscape(chan_q->value(1).toString()));
 | 
						|
  ret.replace("%CATEGORY%",RDXmlEscape(chan_q->value(2).toString()));
 | 
						|
  ret.replace("%LINK%",RDXmlEscape(chan_q->value(3).toString()));
 | 
						|
  ret.replace("%COPYRIGHT%",RDXmlEscape(chan_q->value(4).toString()));
 | 
						|
  ret.replace("%WEBMASTER%",RDXmlEscape(chan_q->value(5).toString()));
 | 
						|
  ret.replace("%LANGUAGE%",RDXmlEscape(chan_q->value(6).toString()));
 | 
						|
  ret.replace("%BUILD_DATE%",chan_q->value(7).toDateTime().
 | 
						|
	      toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT");
 | 
						|
  ret.replace("%PUBLISH_DATE%",chan_q->value(8).toDateTime().
 | 
						|
	      toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT");
 | 
						|
  ret.replace("%GENERATOR%",QString("Rivendell ")+VERSION);
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
QString MainObject::ResolveItemWildcards(const QString &keyname,
 | 
						|
					 RDSqlQuery *item_q,RDSqlQuery *chan_q)
 | 
						|
{
 | 
						|
  RDFeed *feed=new RDFeed(keyname,rda->config());
 | 
						|
  QString ret=chan_q->value(11).toString();
 | 
						|
  ret.replace("%ITEM_TITLE%",RDXmlEscape(item_q->value(0).toString()));
 | 
						|
  ret.replace("%ITEM_DESCRIPTION%",
 | 
						|
	      RDXmlEscape(item_q->value(1).toString()));
 | 
						|
  ret.replace("%ITEM_CATEGORY%",
 | 
						|
	      RDXmlEscape(item_q->value(2).toString()));
 | 
						|
  ret.replace("%ITEM_LINK%",RDXmlEscape(item_q->value(3).toString()));
 | 
						|
  ret.replace("%ITEM_AUTHOR%",RDXmlEscape(item_q->value(4).toString()));
 | 
						|
  ret.replace("%ITEM_SOURCE_TEXT%",
 | 
						|
	      RDXmlEscape(item_q->value(5).toString()));
 | 
						|
  ret.replace("%ITEM_SOURCE_URL%",
 | 
						|
	      RDXmlEscape(item_q->value(6).toString()));
 | 
						|
  ret.replace("%ITEM_COMMENTS%",
 | 
						|
	      RDXmlEscape(item_q->value(7).toString()));
 | 
						|
  ret.replace("%ITEM_AUDIO_URL%",
 | 
						|
	      (const char *)RDXmlEscape(feed->
 | 
						|
	        audioUrl(RDFeed::LinkCounted,server_name,
 | 
						|
			 item_q->value(12).toUInt())));
 | 
						|
  ret.replace("%ITEM_AUDIO_LENGTH%",item_q->value(9).toString());
 | 
						|
  ret.replace("%ITEM_AUDIO_TIME%",
 | 
						|
	      RDGetTimeLength(item_q->value(10).toInt(),false,false));
 | 
						|
  ret.replace("%ITEM_PUBLISH_DATE%",item_q->value(11).toDateTime().
 | 
						|
	      toString("ddd, d MMM yyyy hh:mm:ss ")+"GMT");
 | 
						|
  ret.replace("%ITEM_GUID%",RDPodcast::guid(chan_q->value(12).toString(),
 | 
						|
					    item_q->value(8).toString(),
 | 
						|
					    chan_q->value(11).toUInt(),
 | 
						|
					    item_q->value(12).toUInt()));
 | 
						|
  delete feed;
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool MainObject::ShouldCount(const QString &hdr)
 | 
						|
{
 | 
						|
  bool ret=false;
 | 
						|
  QStringList lines=hdr.split("\n");
 | 
						|
  int n;
 | 
						|
  QString str;
 | 
						|
 | 
						|
  for(int i=0;i<lines.size();i++) {
 | 
						|
    if((n=lines[i].find("="))>0) {
 | 
						|
      if(lines[i].left(n).lower()=="bytes") {
 | 
						|
	str=lines[i].right(lines[i].length()-n-1).stripWhiteSpace();
 | 
						|
	n=str.find("-");
 | 
						|
	if(n==0) {
 | 
						|
	  ret=true;
 | 
						|
	}
 | 
						|
	if(n>0) {
 | 
						|
	  if(str.left(n)=="0") {
 | 
						|
	    ret=true;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void MainObject::Redirect(const QString &url)
 | 
						|
{
 | 
						|
  printf("Status: 301 Moved Permanently\n");
 | 
						|
  printf("Location: %s\n",(const char *)url.utf8());
 | 
						|
  printf("Content-type: text/html\n");
 | 
						|
  printf("\n");
 | 
						|
  printf("The feed has been relocated to %s.\n",(const char *)url.utf8());
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int main(int argc,char *argv[])
 | 
						|
{
 | 
						|
  QApplication a(argc,argv,false);
 | 
						|
  new MainObject();
 | 
						|
  return a.exec();
 | 
						|
}
 |