// rdfeed_script.cpp // // An RSS Feed Generator for Rivendell. // // (C) Copyright 2002-2007,2016-2018 Fred Gleason // // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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
\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
\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("\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("\n"); printf("%s\n",(const char *)ResolveItemWildcards(keyname,q1,q).utf8()); printf("\n"); } delete q1; printf("\n"); printf("\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;i0) { 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(); }