2020-09-21 Fred Gleason <fredg@paravelsystems.com>

* Added a 'PostPodcast' method to the Web API.
	* Added a 'RemovePodcast' method to the Web API.
	* Added a 'PostRss' method to the Web API.
	* Added a 'RemoveRss' method to the Web API.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2020-09-22 15:20:01 -04:00
parent 5349a8e853
commit 5a549b7866
34 changed files with 1163 additions and 281 deletions

View File

@ -20294,3 +20294,8 @@
2020-09-21 Fred Gleason <fredg@paravelsystems.com>
* Added a 'SHA1' column to the 'Podcast Item List' dialog in
rdcastmanager(1).
2020-09-21 Fred Gleason <fredg@paravelsystems.com>
* Added a 'PostPodcast' method to the Web API.
* Added a 'RemovePodcast' method to the Web API.
* Added a 'PostRss' method to the Web API.
* Added a 'RemoveRss' method to the Web API.

View File

@ -839,6 +839,7 @@
</para>
<para>
Required User Permissions: <code>Delete Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the
@ -847,7 +848,7 @@
</para>
<table xml:id="ex.delete_podcast
" frame="all">
<title>GetPodcast Call Fields</title>
<title>DeletePodcast Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
@ -1783,7 +1784,8 @@
<title>GetPodcast</title>
<subtitle>Get posted podcast audio</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_GET_PODCAST</code>
Command Code: <code>RDXPORT_COMMAND_GET_PODCAST</code>, Feed Permissions
or <code>Administer System</code>
</para>
<para>
Required User Permissions: <code>Edit Podcast</code>, Feed Permission
@ -2850,6 +2852,132 @@
</table>
</sect1>
<sect1>
<title>PostPodcast</title>
<subtitle>Upload podcast audio from the audio store to the remote archive</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_POST_PODCAST</code>
</para>
<para>
Required User Permissions: <code>Add Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the
requested podcast's parent feed is not authorized for the specified
Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions.
</para>
<table xml:id="ex.post_podcast
" frame="all">
<title>PostPodcast Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
COMMAND
</entry>
<entry>
40
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
ID
</entry>
<entry>
ID of podcast item (integer)
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>PostRss</title>
<subtitle>Upload podcast RSS XML to the remote archive</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_POST_RSS</code>
</para>
<para>
Required User Permissions: <code>Edit Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the
requested feed is not authorized for the specified
Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions.
</para>
<table xml:id="ex.post_rss
" frame="all">
<title>PostRss Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
COMMAND
</entry>
<entry>
42
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
ID
</entry>
<entry>
ID of podcast feed (integer)
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>Rehash</title>
<subtitle>Generate a SHA-1 hash for a cut and write it to the database</subtitle>
@ -3040,6 +3168,132 @@
</table>
</sect1>
<sect1>
<title>RemovePodcast</title>
<subtitle>Delete podcast audio from the remote archive</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_REMOVE_PODCAST</code>
</para>
<para>
Required User Permissions: <code>Delete Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the
requested podcast's parent feed is not authorized for the specified
Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions.
</para>
<table xml:id="ex.remove_podcast
" frame="all">
<title>RemovePodcast Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
COMMAND
</entry>
<entry>
41
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
ID
</entry>
<entry>
ID of podcast item (integer)
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>RemoveRss</title>
<subtitle>Remove podcast RSS XML from the remote archive</subtitle>
<para>
Command Code: <code>RDXPORT_COMMAND_REMOVE_RSS</code>
</para>
<para>
Required User Permissions: <code>Delete Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the
requested feed is not authorized for the specified
Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions.
</para>
<table xml:id="ex.remove_rss
" frame="all">
<title>RemoveRss Call Fields</title>
<tgroup cols="3" align="left" colsep="1" rowsep="1">
<colspec colname="FIELD NAME" />
<colspec colname="MEANING" />
<colspec colname="REMARKS" />
<thead>
<row>
<entry>
FIELD NAME
</entry>
<entry>
MEANING
</entry>
<entry>
REMARKS
</entry>
</row>
</thead>
<tbody>
<row>
<entry>
COMMAND
</entry>
<entry>
43
</entry>
<entry>
Mandatory
</entry>
</row>
<row>
<entry>
ID
</entry>
<entry>
ID of podcast feed (integer)
</entry>
<entry>
Mandatory
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1>
<title>SaveFile</title>
<subtitle>
@ -3658,6 +3912,7 @@
</para>
<para>
Required User Permissions: <code>Create Podcast</code>, Feed Permission
or <code>Administer System</code>
</para>
<para>
A <computeroutput>404</computeroutput> error will be returned if the

View File

@ -48,23 +48,6 @@
#include "rdwavefile.h"
#include "rdxport_interface.h"
size_t __RDFeed_Readfunction_Callback(char *buffer,size_t size,size_t nitems,
void *userdata)
{
RDFeed *feed=(RDFeed *)userdata;
int curlsize=size*nitems;
int segsize=feed->feed_xml.size()-feed->feed_xml_ptr;
if(segsize<curlsize) {
curlsize=segsize;
}
memcpy(buffer,feed->feed_xml.mid(feed->feed_xml_ptr,curlsize).constData(),
curlsize);
feed->feed_xml_ptr+=curlsize;
return curlsize;
}
RDFeed::RDFeed(const QString &keyname,RDConfig *config,QObject *parent)
: QObject(parent)
{
@ -809,7 +792,7 @@ bool RDFeed::deleteImage(int img_id,QString *err_msg)
}
QString RDFeed::audioUrl(const QString &cgi_hostname,unsigned cast_id)
QString RDFeed::audioUrl(unsigned cast_id)
{
RDPodcast *cast=new RDPodcast(feed_config,cast_id);
QUrl url(baseUrl(cast->feedId()));
@ -844,81 +827,70 @@ QString RDFeed::imageUrl(int img_id) const
bool RDFeed::postXml(QString *err_msg)
{
long response_code;
CURL *curl=NULL;
CURLcode curl_err;
bool ret=false;
char errstr[CURL_ERROR_SIZE];
QDateTime now=QDateTime::currentDateTime();
struct curl_httppost *first=NULL;
struct curl_httppost *last=NULL;
//
// Generate POST Data
//
curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",RDXPORT_COMMAND_POST_RSS),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME",
CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD",
CURLFORM_COPYCONTENTS,
rda->user()->password().toUtf8().constData(),CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",feed_id),
CURLFORM_END);
//
// Set up the transfer
//
if((curl=curl_easy_init())==NULL) {
*err_msg=tr("Unable to get CURL handle.");
curl_formfree(first);
return false;
}
curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);
curl_easy_setopt(curl,CURLOPT_HTTPPOST,first);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent());
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_URL,
rda->station()->webServiceUrl(rda->config()).toUtf8().constData());
//
// Send it
//
if((curl_err=curl_easy_perform(curl))!=CURLE_OK) {
curl_easy_cleanup(curl);
curl_formfree(first);
return false;
}
feed_xml=rssXml(err_msg,now).toUtf8();
feed_xml_ptr=0;
//
// Authentication
// Clean up
//
if((QUrl(feedUrl()).scheme().toLower()=="sftp")&&
(!rda->station()->sshIdentityFile().isEmpty())&&purgeUseIdFile()) {
curl_easy_setopt(curl,CURLOPT_USERNAME,
purgeUsername().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE,
rda->station()->sshIdentityFile().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_KEYPASSWD,
purgePassword().toUtf8().constData());
}
else {
curl_easy_setopt(curl,CURLOPT_USERNAME,
purgeUsername().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_PASSWORD,
purgePassword().toUtf8().constData());
}
curl_easy_setopt(curl,CURLOPT_URL,feedUrl().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_UPLOAD,1);
curl_easy_setopt(curl,CURLOPT_READFUNCTION, __RDFeed_Readfunction_Callback);
curl_easy_setopt(curl,CURLOPT_READDATA,this);
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent().utf8());
curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errstr);
/*
curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,UploadErrorCallback);
*/
switch((curl_err=curl_easy_perform(curl))) {
case CURLE_OK:
case CURLE_PARTIAL_FILE:
setLastBuildDateTime(now);
ret=true;
break;
default:
*err_msg=errstr;
ret=false;
break;
}
curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code);
curl_easy_cleanup(curl);
curl_formfree(first);
//
// Update Enclosing Superfeeds
// Process the results
//
QStringList superfeeds=isSubfeedOf();
for(int i=0;i<superfeeds.size();i++) {
QString err_msg2;
RDFeed *feed=new RDFeed(superfeeds.at(i),feed_config,this);
if(!feed->postXml(&err_msg2)) {
*err_msg+="\n"+err_msg2;
}
delete feed;
if((response_code<200)||(response_code>299)) {
return false;
}
return ret;
return true;
}
@ -936,8 +908,74 @@ bool RDFeed::postXmlConditional(const QString &caption,QWidget *widget)
}
bool RDFeed::deleteXml(QString *err_msg)
bool RDFeed::removeRss(QString *err_msg)
{
long response_code;
CURL *curl=NULL;
CURLcode curl_err;
struct curl_httppost *first=NULL;
struct curl_httppost *last=NULL;
//
// Generate POST Data
//
curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",RDXPORT_COMMAND_REMOVE_RSS),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME",
CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD",
CURLFORM_COPYCONTENTS,
rda->user()->password().toUtf8().constData(),CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",feed_id),
CURLFORM_END);
//
// Set up the transfer
//
if((curl=curl_easy_init())==NULL) {
curl_formfree(first);
return false;
}
curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);
curl_easy_setopt(curl,CURLOPT_HTTPPOST,first);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent());
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_URL,
rda->station()->webServiceUrl(rda->config()).toUtf8().constData());
//
// Send it
//
if((curl_err=curl_easy_perform(curl))!=CURLE_OK) {
curl_easy_cleanup(curl);
curl_formfree(first);
return false;
}
//
// Clean up
//
curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code);
curl_easy_cleanup(curl);
curl_formfree(first);
//
// Process the results
//
if((response_code<200)||(response_code>299)) {
return false;
}
return true;
/*
RDDelete::ErrorCode conv_err;
RDDelete *conv=new RDDelete(rda->config());
if(!conv->urlIsSupported(feedUrl())) {
@ -954,6 +992,7 @@ bool RDFeed::deleteXml(QString *err_msg)
delete conv;
return conv_err==RDDelete::ErrorOk;
*/
}
@ -995,11 +1034,7 @@ unsigned RDFeed::postCut(const QString &cutname,Error *err)
QString err_msg;
QString tmpfile;
QString destfile;
QString sql;
RDSqlQuery *q;
RDPodcast *cast=NULL;
RDUpload *upload=NULL;
RDUpload::ErrorCode upload_err;
RDAudioConvert::ErrorCode audio_conv_err;
RDAudioExport::ErrorCode export_err;
@ -1060,47 +1095,23 @@ unsigned RDFeed::postCut(const QString &cutname,Error *err)
}
delete settings;
delete conv;
//
// Upload
//
emit postProgressChanged(2);
QFile file(tmpfile);
int length=file.size();
unsigned cast_id=CreateCast(&destfile,length,cut->length());
delete cut;
cast=new RDPodcast(feed_config,cast_id);
upload=new RDUpload(rda->config(),this);
upload->setSourceFile(tmpfile);
upload->setDestinationUrl(purgeUrl()+"/"+cast->audioFilename());
switch((upload_err=upload->runUpload(purgeUsername(),purgePassword(),
rda->station()->sshIdentityFile(),
purgeUseIdFile(),
rda->config()->logXloadDebugData()))) {
case RDUpload::ErrorOk:
*err=RDFeed::ErrorOk;
break;
default:
*err=RDFeed::ErrorUploadFailed;
sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id);
q=new RDSqlQuery(sql);
delete q;
delete upload;
delete cast;
*err=RDFeed::ErrorUploadFailed;
unlink(tmpfile);
emit postProgressChanged(5);
return 0;
}
emit postProgressChanged(3);
postProgressChanged(2);
//
// Save to Audio Store
//
QFile file(tmpfile);
int length=file.size();
unsigned cast_id=CreateCast(&destfile,length,cut->length());
cast=new RDPodcast(feed_config,cast_id);
SavePodcast(cast_id,tmpfile);
unlink(tmpfile);
delete upload;
//
// Upload to remote archive
//
PostPodcast(cast_id);
postProgressChanged(3);
//
// Set default cast parameters
@ -1109,11 +1120,14 @@ unsigned RDFeed::postCut(const QString &cutname,Error *err)
cast->setItemTitle(cart->title());
cast->setItemImageId(defaultItemImageId());
delete cart;
delete cut;
delete cast;
emit postProgressChanged(4);
postXml(&err_msg);
//
// Update posted XML
//
postXml(&err_msg);
emit postProgressChanged(5);
return cast_id;
@ -1123,20 +1137,16 @@ unsigned RDFeed::postCut(const QString &cutname,Error *err)
unsigned RDFeed::postFile(const QString &srcfile,Error *err)
{
QString err_msg;
QString sql;
RDSqlQuery *q;
QString cmd;
QString tmpfile;
QString tmpfile2;
QString destfile;
int time_length=0;
RDUpload *upload=NULL;
RDUpload::ErrorCode upload_err;
RDWaveFile *wave=NULL;
RDWaveData wavedata;
unsigned audio_time=0;
emit postProgressRangeChanged(0,4);
emit postProgressRangeChanged(0,6);
emit postProgressChanged(0);
//
@ -1153,7 +1163,6 @@ unsigned RDFeed::postFile(const QString &srcfile,Error *err)
settings->setBitRate(uploadBitRate());
settings->setNormalizationLevel(normalizeLevel()/100);
conv->setDestinationSettings(settings);
emit postProgressChanged(1);
switch(conv->convert()) {
@ -1171,7 +1180,7 @@ unsigned RDFeed::postFile(const QString &srcfile,Error *err)
delete conv;
*err=RDFeed::ErrorUnsupportedType;
unlink(tmpfile);
emit postProgressChanged(4);
emit postProgressChanged(6);
return 0;
case RDAudioConvert::ErrorNoSource:
@ -1187,56 +1196,30 @@ unsigned RDFeed::postFile(const QString &srcfile,Error *err)
delete conv;
*err=RDFeed::ErrorGeneral;
unlink(tmpfile);
emit postProgressChanged(4);
emit postProgressChanged(6);
return 0;
}
delete settings;
delete conv;
//
// Upload
//
emit postProgressChanged(2);
QFile file(tmpfile);
int length=file.size();
unsigned cast_id=CreateCast(&destfile,length,time_length);
RDPodcast *cast=new RDPodcast(feed_config,cast_id);
upload=new RDUpload(rda->config(),this);
upload->setSourceFile(tmpfile);
upload->setDestinationUrl(purgeUrl()+"/"+cast->audioFilename());
switch((upload_err=upload->runUpload(purgeUsername(),purgePassword(),
rda->station()->sshIdentityFile(),
purgeUseIdFile(),
rda->config()->logXloadDebugData()))) {
case RDUpload::ErrorOk:
sql=QString().sprintf("update PODCASTS set AUDIO_TIME=%u where ID=%u",
audio_time,cast_id);
q=new RDSqlQuery(sql);
delete q;
break;
default:
*err=RDFeed::ErrorUploadFailed;
sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id);
q=new RDSqlQuery(sql);
delete q;
delete upload;
delete cast;
*err=RDFeed::ErrorUploadFailed;
unlink(tmpfile);
emit postProgressChanged(4);
return 0;
}
delete upload;
//
// Save to Audio Store
//
QFile file(tmpfile);
int length=file.size();
unsigned cast_id=CreateCast(&destfile,length,time_length);
RDPodcast *cast=new RDPodcast(feed_config,cast_id);
SavePodcast(cast_id,tmpfile);
unlink(QString(tmpfile)+".wav");
unlink(tmpfile);
emit postProgressChanged(3);
//
// Upload to remote archive
//
PostPodcast(cast_id);
postProgressChanged(4);
//
// Set default cast parameters
@ -1247,13 +1230,17 @@ unsigned RDFeed::postFile(const QString &srcfile,Error *err)
else {
cast->setItemTitle(srcfile.split("/").last());
}
cast->setAudioTime(audio_time);
cast->setItemImageId(defaultItemImageId());
delete cast;
emit postProgressChanged(5);
emit postProgressChanged(3);
//
//
// Update posted XML
//
postXml(&err_msg);
emit postProgressChanged(4);
emit postProgressChanged(6);
*err=RDFeed::ErrorOk;
return cast_id;
@ -1264,17 +1251,17 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time,
bool stop_at_stop,int start_line,int end_line,
RDFeed::Error *err)
{
QString sql;
RDSqlQuery *q=NULL;
QString tmpfile;
QString destfile;
QString err_msg;
RDUpload *upload=NULL;
RDUpload::ErrorCode upload_err;
RDRenderer *renderer=NULL;
RDSettings *settings=NULL;
RDLogEvent *log_event=NULL;
feed_render_start_line=start_line;
feed_render_end_line=end_line;
emit postProgressRangeChanged(0,4+(end_line-start_line));
emit postProgressChanged(0);
//
@ -1288,8 +1275,6 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time,
return 0;
}
emit postProgressRangeChanged(0,3+log_event->size());
//
// Render Log
//
@ -1301,7 +1286,6 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time,
settings->setSampleRate(uploadSampleRate());
settings->setBitRate(uploadBitRate());
settings->setNormalizationLevel(normalizeLevel()/100);
renderer=new RDRenderer(this);
connect(renderer,SIGNAL(progressMessageSent(const QString &)),
this,SLOT(renderMessage(const QString &)));
@ -1318,52 +1302,24 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time,
return 0;
}
delete renderer;
emit postProgressChanged(1+log_event->size());
emit postProgressChanged(1+(end_line-start_line));
//
// Upload Rendered File
// Save to Audio Store
//
QFile f(tmpfile);
unsigned cast_id=
CreateCast(&destfile,f.size(),log_event->length(0,log_event->size()));
RDPodcast *cast=new RDPodcast(feed_config,cast_id);
upload=new RDUpload(rda->config(),this);
upload->setSourceFile(tmpfile);
upload->setDestinationUrl(purgeUrl()+"/"+cast->audioFilename());
switch((upload_err=upload->runUpload(purgeUsername(),purgePassword(),
rda->station()->sshIdentityFile(),
purgeUseIdFile(),
rda->config()->logXloadDebugData()))) {
case RDUpload::ErrorOk:
sql=QString().sprintf("update PODCASTS set AUDIO_TIME=%u where ID=%u",
log_event->length(0,log_event->size()),cast_id);
q=new RDSqlQuery(sql);
delete q;
break;
default:
*err=RDFeed::ErrorUploadFailed;
sql=QString().sprintf("delete from PODCASTS where ID=%u",cast_id);
q=new RDSqlQuery(sql);
delete q;
delete upload;
delete cast;
delete settings;
delete log_event;
*err=RDFeed::ErrorUploadFailed;
unlink(tmpfile);
emit postProgressChanged(3+log_event->size());
return 0;
}
emit postProgressChanged(2+log_event->size());
//
// Save to Audio Store
//
SavePodcast(cast_id,tmpfile);
unlink(tmpfile);
emit postProgressChanged(2+(end_line-start_line));
//
// Save to remote archive
//
PostPodcast(cast_id);
emit postProgressChanged(3+(end_line-start_line));
//
// Set default cast parameters
@ -1376,16 +1332,14 @@ unsigned RDFeed::postLog(const QString &logname,const QTime &start_time,
cast->setItemTitle(log->description());
}
cast->setItemImageId(defaultItemImageId());
cast->setAudioTime(log_event->length(start_line,1+end_line));
delete log;
postXml(&err_msg);
*err=RDFeed::ErrorOk;
emit postProgressChanged(3+log_event->size());
emit postProgressChanged(4+(end_line-start_line));
delete cast;
delete upload;
delete settings;
delete log_event;
unlink(tmpfile);
@ -1683,7 +1637,9 @@ void RDFeed::renderMessage(const QString &msg)
void RDFeed::renderLineStartedData(int lineno,int total_lines)
{
emit postProgressChanged(lineno+1);
if((lineno>=feed_render_start_line)&&(lineno<=feed_render_end_line)) {
emit postProgressChanged(1+(lineno-feed_render_start_line));
}
}
@ -1761,6 +1717,75 @@ bool RDFeed::SavePodcast(unsigned cast_id,const QString &src_filename) const
}
bool RDFeed::PostPodcast(unsigned cast_id) const
{
long response_code;
CURL *curl=NULL;
CURLcode curl_err;
struct curl_httppost *first=NULL;
struct curl_httppost *last=NULL;
//
// Generate POST Data
//
curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",RDXPORT_COMMAND_POST_PODCAST),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME",
CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD",
CURLFORM_COPYCONTENTS,
rda->user()->password().toUtf8().constData(),CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",cast_id),
CURLFORM_END);
//
// Set up the transfer
//
if((curl=curl_easy_init())==NULL) {
curl_formfree(first);
return false;
}
curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);
curl_easy_setopt(curl,CURLOPT_HTTPPOST,first);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent());
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_URL,
rda->station()->webServiceUrl(rda->config()).toUtf8().constData());
//
// Send it
//
if((curl_err=curl_easy_perform(curl))!=CURLE_OK) {
curl_easy_cleanup(curl);
curl_formfree(first);
return false;
}
//
// Clean up
//
curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code);
curl_easy_cleanup(curl);
curl_formfree(first);
//
// Process the results
//
if((response_code<200)||(response_code>299)) {
return false;
}
return true;
}
unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const
{
QString sql;
@ -1924,8 +1949,7 @@ QString RDFeed::ResolveItemWildcards(const QString &tmplt,RDSqlQuery *item_q,
}
ret.replace("%ITEM_EXPLICIT%",explicit_str);
ret.replace("%ITEM_AUDIO_URL%",
RDXmlEscape(audioUrl(feed_cgi_hostname,
item_q->value(14).toUInt())));
RDXmlEscape(audioUrl(item_q->value(14).toUInt())));
ret.replace("%ITEM_AUDIO_LENGTH%",item_q->value(11).toString());
ret.replace("%ITEM_AUDIO_TIME%",
RDGetTimeLength(item_q->value(12).toInt(),false,false));

View File

@ -132,11 +132,12 @@ class RDFeed : public QObject
int importImageFile(const QString &pathname,QString *err_msg,
QString desc="") const;
bool deleteImage(int img_id,QString *err_msg);
QString audioUrl(const QString &cgi_hostname,unsigned cast_id);
QString audioUrl(unsigned cast_id);
QString imageUrl(int img_id) const;
bool postXml(QString *err_msg);
bool postXmlConditional(const QString &caption,QWidget *widget);
bool deleteXml(QString *err_msg);
//bool deleteXml(QString *err_msg);
bool removeRss(QString *err_msg);
bool deleteImages(QString *err_msg);
unsigned postCut(const QString &cutname,Error *err);
unsigned postFile(const QString &srcfile,Error *err);
@ -161,6 +162,7 @@ class RDFeed : public QObject
private:
bool SavePodcast(unsigned cast_id,const QString &src_filename) const;
bool PostPodcast(unsigned cast_id) const;
unsigned CreateCast(QString *filename,int bytes,int msecs) const;
QString ResolveChannelWildcards(const QString &tmplt,RDSqlQuery *chan_q,
const QDateTime &build_datetime);
@ -178,8 +180,8 @@ class RDFeed : public QObject
RDConfig *feed_config;
QByteArray feed_xml;
int feed_xml_ptr;
friend size_t __RDFeed_Readfunction_Callback(char *buffer,size_t size,
size_t nitems,void *userdata);
int feed_render_start_line;
int feed_render_end_line;
};

View File

@ -362,30 +362,12 @@ void RDPodcast::setStatus(RDPodcast::Status status)
}
bool RDPodcast::removeAudio(RDFeed *feed,QString *err_text,bool log_debug) const
bool RDPodcast::dropAudio(RDFeed *feed,QString *err_text,bool log_debug) const
{
RDDelete::ErrorCode conv_err;
QUrl url(feed->purgeUrl()+"/"+audioFilename());
RDDelete *conv=new RDDelete(rda->config());
if(!conv->urlIsSupported(url)) {
*err_text="unsupported url scheme";
delete conv;
if(!RemovePodcast(podcast_id)) {
return false;
}
conv->setTargetUrl(url);
conv_err=conv->runDelete(feed->purgeUsername(),feed->purgePassword(),
rda->station()->sshIdentityFile(),
feed->purgeUseIdFile(),
rda->config()->logXloadDebugData());
*err_text=RDDelete::errorText(conv_err);
delete conv;
//
// Delete from Audio Store
//
DeletePodcast(id());
return conv_err==RDDelete::ErrorOk;
return DeletePodcast(podcast_id);
}
@ -472,6 +454,75 @@ bool RDPodcast::DeletePodcast(unsigned cast_id) const
}
bool RDPodcast::RemovePodcast(unsigned cast_id) const
{
long response_code;
CURL *curl=NULL;
CURLcode curl_err;
struct curl_httppost *first=NULL;
struct curl_httppost *last=NULL;
//
// Generate POST Data
//
curl_formadd(&first,&last,CURLFORM_PTRNAME,"COMMAND",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",RDXPORT_COMMAND_REMOVE_PODCAST),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"LOGIN_NAME",
CURLFORM_COPYCONTENTS,rda->user()->name().toUtf8().constData(),
CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"PASSWORD",
CURLFORM_COPYCONTENTS,
rda->user()->password().toUtf8().constData(),CURLFORM_END);
curl_formadd(&first,&last,CURLFORM_PTRNAME,"ID",
CURLFORM_COPYCONTENTS,
(const char *)QString().sprintf("%u",cast_id),
CURLFORM_END);
//
// Set up the transfer
//
if((curl=curl_easy_init())==NULL) {
curl_formfree(first);
return false;
}
curl_easy_setopt(curl,CURLOPT_WRITEDATA,stdout);
curl_easy_setopt(curl,CURLOPT_HTTPPOST,first);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent());
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_URL,
rda->station()->webServiceUrl(rda->config()).toUtf8().constData());
//
// Send it
//
if((curl_err=curl_easy_perform(curl))!=CURLE_OK) {
curl_easy_cleanup(curl);
curl_formfree(first);
return false;
}
//
// Clean up
//
curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code);
curl_easy_cleanup(curl);
curl_formfree(first);
//
// Process the results
//
if((response_code<200)||(response_code>299)) {
return false;
}
return true;
}
void RDPodcast::SetRow(const QString &param,int value) const
{
QString sql;

View File

@ -74,7 +74,7 @@ class RDPodcast
void setExpirationDateTime(const QDateTime &dt) const;
RDPodcast::Status status() const;
void setStatus(RDPodcast::Status status);
bool removeAudio(RDFeed *feed,QString *err_text,bool log_debug) const;
bool dropAudio(RDFeed *feed,QString *err_text,bool log_debug) const;
static QString guid(const QString &url,const QString &filename,
unsigned feed_id,unsigned cast_id);
static QString guid(const QString &full_url,
@ -82,6 +82,7 @@ class RDPodcast
private:
bool DeletePodcast(unsigned cast_id) const;
bool RemovePodcast(unsigned cast_id) const;
void SetRow(const QString &param,int value) const;
void SetRow(const QString &param,const QString &value) const;
void SetRow(const QString &param,const QDateTime &datetime,const QString &value) const;

View File

@ -60,6 +60,10 @@
#define RDXPORT_COMMAND_GET_PODCAST 37
#define RDXPORT_COMMAND_SAVE_PODCAST 38
#define RDXPORT_COMMAND_DELETE_PODCAST 39
#define RDXPORT_COMMAND_POST_PODCAST 40
#define RDXPORT_COMMAND_REMOVE_PODCAST 41
#define RDXPORT_COMMAND_POST_RSS 42
#define RDXPORT_COMMAND_REMOVE_RSS 43
#endif // RDXPORT_INTERFACE_H

View File

@ -72,6 +72,14 @@ ListFeeds::ListFeeds(QWidget *parent)
list_delete_button->setText(tr("&Delete"));
connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData()));
//
// Repost Button
//
list_repost_button=new QPushButton(this);
list_repost_button->setFont(buttonFont());
list_repost_button->setText(tr("&Repost"));
connect(list_repost_button,SIGNAL(clicked()),this,SLOT(repostData()));
//
// Close Button
//
@ -118,7 +126,7 @@ ListFeeds::~ListFeeds()
QSize ListFeeds::sizeHint() const
{
return QSize(800,300);
return QSize(800,390);
}
@ -231,7 +239,7 @@ void ListFeeds::deleteData()
pd->setValue(pd->value()+1);
qApp->processEvents();
cast=new RDPodcast(rda->config(),q->value(0).toUInt());
cast->removeAudio(feed,&errs,rda->config()->logXloadDebugData());
cast->dropAudio(feed,&errs,rda->config()->logXloadDebugData());
delete cast;
}
delete q;
@ -239,7 +247,7 @@ void ListFeeds::deleteData()
//
// Delete Remote XML
//
if(!feed->deleteXml(&errs)) {
if(!feed->removeRss(&errs)) {
QMessageBox::warning(this,"RDAdmin - "+tr("Warning"),
tr("Failed to delete remote feed XML.")+
"["+errs+"].");
@ -293,6 +301,11 @@ void ListFeeds::doubleClickedData(Q3ListViewItem *item,const QPoint &pt,
}
void ListFeeds::repostData()
{
}
void ListFeeds::closeData()
{
done(0);
@ -304,6 +317,7 @@ void ListFeeds::resizeEvent(QResizeEvent *e)
list_add_button->setGeometry(size().width()-90,30,80,50);
list_edit_button->setGeometry(size().width()-90,90,80,50);
list_delete_button->setGeometry(size().width()-90,150,80,50);
list_repost_button->setGeometry(size().width()-90,240,80,50);
list_close_button->setGeometry(size().width()-90,size().height()-60,80,50);
list_feeds_view->setGeometry(10,30,size().width()-120,size().height()-40);
}

View File

@ -40,6 +40,7 @@ class ListFeeds : public RDDialog
void editData();
void deleteData();
void doubleClickedData(Q3ListViewItem *item,const QPoint &pt,int col);
void repostData();
void closeData();
protected:
@ -52,6 +53,7 @@ class ListFeeds : public RDDialog
QPushButton *list_add_button;
QPushButton *list_edit_button;
QPushButton *list_delete_button;
QPushButton *list_repost_button;
QPushButton *list_close_button;
};

View File

@ -5208,6 +5208,10 @@ Stále ještě jej chcete smazat?</translation>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished">&amp;Vyvěsit znovu</translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -4974,6 +4974,10 @@ Wollen Sie ihn immernoch löschen?</translation>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -5171,6 +5171,10 @@ Do you still want to delete it?</source>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished">&amp;Republicar</translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -4120,6 +4120,10 @@ Permissions</source>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -4835,6 +4835,10 @@ Klikk på &quot;Lisens&quot;-knappen for fleire opplysningar.</translation>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -4835,6 +4835,10 @@ Klikk på &quot;Lisens&quot;-knappen for fleire opplysningar.</translation>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -4958,6 +4958,10 @@ Você ainda quer Deletar?</translation>
<source>Public URL</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Repost</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListGpis</name>

View File

@ -390,10 +390,9 @@ void ListCasts::deleteData()
sleep(1);
qApp->processEvents();
RDPodcast *cast=new RDPodcast(rda->config(),item->id());
if(!cast->removeAudio(list_feed,&err_text,
rda->config()->logXloadDebugData())) {
if(!cast->dropAudio(list_feed,&err_text,rda->config()->logXloadDebugData())) {
if(QMessageBox::warning(this,"RDCastManager - "+tr("Remote Error"),
tr("Unable to delete remote audio!\n")+
tr("Unable to drop remote audio!\n")+
tr("The server said: \"")+err_text+"\".\n\n"+
tr("Continue deleting cast?"),
QMessageBox::Yes,QMessageBox::No)==QMessageBox::No) {

View File

@ -330,7 +330,7 @@ Podcast trotzdem löschen?</translation>
<message>
<source>Unable to delete remote audio!
</source>
<translation>Nelze smazat soubory se zvukem na jiném serveru!</translation>
<translation type="obsolete">Nelze smazat soubory se zvukem na jiném serveru!</translation>
</message>
<message>
<source>The server said: &quot;</source>
@ -414,6 +414,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -315,7 +315,7 @@ Podcast trotzdem löschen?</translation>
<message>
<source>Unable to delete remote audio!
</source>
<translation>Kann die Audiodatei(en) auf dem anderen Server nicht löschen!</translation>
<translation type="obsolete">Kann die Audiodatei(en) auf dem anderen Server nicht löschen!</translation>
</message>
<message>
<source>The server said: &quot;</source>
@ -399,6 +399,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -269,7 +269,7 @@ Suscripción</translation>
<message>
<source>Unable to delete remote audio!
</source>
<translation>¡No fue posible eliminar audio remoto!
<translation type="obsolete">¡No fue posible eliminar audio remoto!
</translation>
</message>
<message>
@ -354,6 +354,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -168,11 +168,6 @@ Car&amp;t/Cut</source>
<source>Remote Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote audio!
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The server said: &quot;</source>
<translation type="unfinished"></translation>
@ -255,6 +250,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -299,11 +299,6 @@ Vil du halda fram med å sletta podkasten?</translation>
<source>Posting Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote audio!
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The server said: &quot;</source>
<translation type="unfinished"></translation>
@ -386,6 +381,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -299,11 +299,6 @@ Vil du halda fram med å sletta podkasten?</translation>
<source>Posting Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote audio!
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The server said: &quot;</source>
<translation type="unfinished"></translation>
@ -386,6 +381,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -277,11 +277,6 @@ Continuar deletando cast?</translation>
<source>Never</source>
<translation>Nunca</translation>
</message>
<message>
<source>Unable to delete remote audio!
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The server said: &quot;</source>
<translation type="unfinished"></translation>
@ -364,6 +359,11 @@ Log</source>
<source>[none]</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to drop remote audio!
</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>LogDialog</name>

View File

@ -148,7 +148,7 @@ void MainObject::ProcessFeed(const QString &key_name)
bool deleted=false;
if(q->value(1).toDateTime()<now) { // Delete expired cast
RDPodcast *cast=new RDPodcast(rda->config(),q->value(0).toUInt());
if(!cast->removeAudio(feed,&err_msg,false)) {
if(!cast->dropAudio(feed,&err_msg,false)) {
rda->syslog(LOG_WARNING,
"audio purge failed for cast %u [%s] on feed \"%s\" [%s]",
q->value(0).toUInt(),

View File

@ -1437,7 +1437,7 @@ void MainObject::DeleteCast()
RDFeed *feed=new RDFeed(cast_feed_id,rda->config());
RDPodcast *cast=new RDPodcast(rda->config(),cast_cast_id);
cast->removeAudio(feed,&errs,rda->config()->logXloadDebugData());
cast->dropAudio(feed,&errs,rda->config()->logXloadDebugData());
delete cast;
delete feed;

View File

@ -24,18 +24,40 @@
#include <fcntl.h>
#include <errno.h>
#include <curl/curl.h>
#include <rdapplication.h>
#include <rdconf.h>
#include <rddelete.h>
#include <rdescape_string.h>
#include <rdfeed.h>
#include <rdformpost.h>
#include <rdgroup.h>
#include <rdhash.h>
#include <rdpodcast.h>
#include <rdupload.h>
#include <rduser.h>
#include <rdweb.h>
#include "rdxport.h"
size_t __PostRss_Readfunction_Callback(char *buffer,size_t size,size_t nitems,
void *userdata)
{
Xport *xport=(Xport *)userdata;
int curlsize=size*nitems;
int segsize=xport->xport_curl_data.size()-xport->xport_curl_data_ptr;
if(segsize<curlsize) {
curlsize=segsize;
}
memcpy(buffer,xport->xport_curl_data.mid(xport->xport_curl_data_ptr,curlsize).constData(),
curlsize);
xport->xport_curl_data_ptr+=curlsize;
return curlsize;
}
void Xport::SavePodcast()
{
int cast_id=0;
@ -64,7 +86,9 @@ void Xport::SavePodcast()
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=cast->keyName();
if((!rda->user()->addPodcast())||(!rda->user()->feedAuthorized(keyname))) {
if(((!rda->user()->addPodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
@ -122,7 +146,9 @@ void Xport::GetPodcast()
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=cast->keyName();
if((!rda->user()->editPodcast())||(!rda->user()->feedAuthorized(keyname))) {
if(((!rda->user()->addPodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
@ -180,7 +206,9 @@ void Xport::DeletePodcast()
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=cast->keyName();
if((!rda->user()->deletePodcast())||(!rda->user()->feedAuthorized(keyname))) {
if(((!rda->user()->deletePodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
@ -205,3 +233,289 @@ void Xport::DeletePodcast()
Exit(0);
}
void Xport::PostPodcast()
{
int cast_id=0;
QString keyname;
QString destpath;
QString err_msg;
RDPodcast *cast=NULL;
RDFeed *feed=NULL;
QString msg="OK";
RDUpload::ErrorCode upload_err;
if(!xport_post->getValue("ID",&cast_id)) {
XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER);
}
cast=new RDPodcast(rda->config(),cast_id);
if(!cast->exists()) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=cast->keyName();
if(((!rda->user()->addPodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename();
feed=new RDFeed(keyname,rda->config(),this);
RDUpload *upload=new RDUpload(rda->config(),this);
upload->setSourceFile(destpath);
QString desturl=feed->purgeUrl()+"/"+cast->audioFilename();
upload->setDestinationUrl(desturl);
if((upload_err=upload->
runUpload(feed->purgeUsername(),feed->purgePassword(),
rda->station()->sshIdentityFile(),feed->purgeUseIdFile(),
rda->config()->logXloadDebugData()))!=RDUpload::ErrorOk) {
delete upload;
delete feed;
delete cast;
XmlExit(QString("Upload to \"")+desturl+"\" failed ["+
RDUpload::errorText(upload_err)+"]",500,"podcasts.cpp",LINE_NUMBER);
}
delete upload;
printf("Content-type: text/html; charset: UTF-8\n");
printf("Status: 200\n\n");
printf("OK\n");
rda->syslog(LOG_DEBUG,
"posted podcast audio \"%s\"",destpath.toUtf8().constData());
delete feed;
delete cast;
Exit(0);
}
void Xport::RemovePodcast()
{
int cast_id=0;
QString keyname;
QString destpath;
QString err_msg;
RDPodcast *cast=NULL;
RDFeed *feed=NULL;
QString msg="OK";
RDDelete::ErrorCode del_err;
if(!xport_post->getValue("ID",&cast_id)) {
XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER);
}
cast=new RDPodcast(rda->config(),cast_id);
if(!cast->exists()) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=cast->keyName();
if(((!rda->user()->deletePodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete cast;
XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER);
}
destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename();
feed=new RDFeed(keyname,rda->config(),this);
RDDelete *del=new RDDelete(rda->config(),this);
QString desturl=feed->purgeUrl()+"/"+cast->audioFilename();
del->setTargetUrl(desturl);
if((del_err=del->
runDelete(feed->purgeUsername(),feed->purgePassword(),
rda->station()->sshIdentityFile(),feed->purgeUseIdFile(),
rda->config()->logXloadDebugData()))!=RDDelete::ErrorOk) {
delete del;
delete feed;
delete cast;
XmlExit(QString("Deletion of \"")+desturl+"\" failed ["+
RDDelete::errorText(del_err)+"]",500,"podcasts.cpp",LINE_NUMBER);
}
delete del;
printf("Content-type: text/html; charset: UTF-8\n");
printf("Status: 200\n\n");
printf("OK\n");
rda->syslog(LOG_DEBUG,
"delete podcast audio \"%s\"",destpath.toUtf8().constData());
delete feed;
delete cast;
Exit(0);
}
void Xport::PostRss()
{
int feed_id=0;
QString keyname;
QString destpath;
QString err_msg;
RDFeed *feed=NULL;
QString msg="OK";
bool ret=false;
CURL *curl=NULL;
CURLcode curl_err;
char errstr[CURL_ERROR_SIZE];
QDateTime now=QDateTime::currentDateTime();
if(!xport_post->getValue("ID",&feed_id)) {
XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER);
}
feed=new RDFeed(feed_id,rda->config(),this);
if(!feed->exists()) {
XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=feed->keyName();
if(((!rda->user()->editPodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete feed;
XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER);
}
if((curl=curl_easy_init())==NULL) {
XmlExit("unable to get CURL handle",500,"podcasts.cpp",LINE_NUMBER);
}
xport_curl_data=feed->rssXml(&err_msg,now).toUtf8();
xport_curl_data_ptr=0;
//
// Authentication
//
if((QUrl(feed->feedUrl()).scheme().toLower()=="sftp")&&
(!rda->station()->sshIdentityFile().isEmpty())&&feed->purgeUseIdFile()) {
curl_easy_setopt(curl,CURLOPT_USERNAME,
feed->purgeUsername().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE,
rda->station()->sshIdentityFile().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_KEYPASSWD,
feed->purgePassword().toUtf8().constData());
}
else {
curl_easy_setopt(curl,CURLOPT_USERNAME,
feed->purgeUsername().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_PASSWORD,
feed->purgePassword().toUtf8().constData());
}
curl_easy_setopt(curl,CURLOPT_URL,feed->feedUrl().toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_UPLOAD,1);
curl_easy_setopt(curl,CURLOPT_READFUNCTION, __PostRss_Readfunction_Callback);
curl_easy_setopt(curl,CURLOPT_READDATA,this);
curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT);
curl_easy_setopt(curl,CURLOPT_NOPROGRESS,1);
curl_easy_setopt(curl,CURLOPT_USERAGENT,
(const char *)rda->config()->userAgent().utf8());
curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errstr);
//curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
//curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,UploadErrorCallback);
switch((curl_err=curl_easy_perform(curl))) {
case CURLE_OK:
case CURLE_PARTIAL_FILE:
feed->setLastBuildDateTime(now);
ret=true;
break;
default:
err_msg=errstr;
ret=false;
break;
}
curl_easy_cleanup(curl);
//
// Update Enclosing Superfeeds
//
QStringList superfeeds=feed->isSubfeedOf();
for(int i=0;i<superfeeds.size();i++) {
QString err_msg2;
RDFeed *feed=new RDFeed(superfeeds.at(i),rda->config(),this);
if(!feed->postXml(&err_msg2)) {
err_msg+="\n"+err_msg2;
}
delete feed;
}
if(!ret) {
XmlExit(err_msg,500,"podcasts.cpp",LINE_NUMBER);
}
printf("Content-type: text/html; charset: UTF-8\n");
printf("Status: 200\n\n");
printf("OK\n");
rda->syslog(LOG_DEBUG,
"posted RSS XML to \"%s\"",feed->feedUrl().toUtf8().constData());
Exit(0);
}
void Xport::RemoveRss()
{
int feed_id=0;
RDFeed *feed=NULL;
QString keyname;
QString destpath;
QString err_msg;
// RDPodcast *cast=NULL;
QString msg="OK";
RDDelete::ErrorCode del_err;
if(!xport_post->getValue("ID",&feed_id)) {
XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER);
}
feed=new RDFeed(feed_id,rda->config(),this);
if(!feed->exists()) {
XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER);
}
keyname=feed->keyName();
if(((!rda->user()->deletePodcast())||
(!rda->user()->feedAuthorized(keyname)))&&
(!rda->user()->adminConfig())) {
delete feed;
XmlExit("No such feed",404,"podcasts.cpp",LINE_NUMBER);
}
RDDelete *del=new RDDelete(rda->config(),this);
QString desturl=feed->feedUrl();
del->setTargetUrl(desturl);
if((del_err=del->
runDelete(feed->purgeUsername(),feed->purgePassword(),
rda->station()->sshIdentityFile(),feed->purgeUseIdFile(),
rda->config()->logXloadDebugData()))!=RDDelete::ErrorOk) {
delete del;
delete feed;
XmlExit(QString("Deletion of \"")+desturl+"\" failed ["+
RDDelete::errorText(del_err)+"]",500,"podcasts.cpp",LINE_NUMBER);
}
delete del;
printf("Content-type: text/html; charset: UTF-8\n");
printf("Status: 200\n\n");
printf("OK\n");
rda->syslog(LOG_DEBUG,
"deleted podcast RSS \"%s\"",destpath.toUtf8().constData());
delete feed;
Exit(0);
}

View File

@ -308,6 +308,22 @@ void Xport::ripcConnectedData(bool state)
DeletePodcast();
break;
case RDXPORT_COMMAND_POST_PODCAST:
PostPodcast();
break;
case RDXPORT_COMMAND_REMOVE_PODCAST:
RemovePodcast();
break;
case RDXPORT_COMMAND_POST_RSS:
PostRss();
break;
case RDXPORT_COMMAND_REMOVE_RSS:
RemoveRss();
break;
default:
printf("Content-type: text/html\n\n");
printf("rdxport: missing/invalid command\n");

View File

@ -85,6 +85,10 @@ class Xport : public QObject
void SavePodcast();
void GetPodcast();
void DeletePodcast();
void PostPodcast();
void RemovePodcast();
void PostRss();
void RemoveRss();
void LockLog();
QString LogLockXml(bool result,const QString &log_name,const QString &guid,
const QString &username,const QString &stationname,
@ -100,6 +104,10 @@ class Xport : public QObject
RDFormPost *xport_post;
QString xport_remote_hostname;
QHostAddress xport_remote_address;
QByteArray xport_curl_data;
int xport_curl_data_ptr;
friend size_t __PostRss_Readfunction_Callback(char *buffer,size_t size,
size_t nitems,void *userdata);
};

View File

@ -53,9 +53,13 @@ install-exec-am:
cp listservices.html $(DESTDIR)@libexecdir@
cp listsystemsettings.html $(DESTDIR)@libexecdir@
cp locklog.html $(DESTDIR)@libexecdir@
cp postpodcast.html $(DESTDIR)@libexecdir@
cp postrss.html $(DESTDIR)@libexecdir@
cp rehash.html $(DESTDIR)@libexecdir@
cp removecart.html $(DESTDIR)@libexecdir@
cp removecut.html $(DESTDIR)@libexecdir@
cp removepodcast.html $(DESTDIR)@libexecdir@
cp removerss.html $(DESTDIR)@libexecdir@
cp savefile.html $(DESTDIR)@libexecdir@
cp savelog.html $(DESTDIR)@libexecdir@
cp savepodcast.html $(DESTDIR)@libexecdir@
@ -97,9 +101,13 @@ uninstall-local:
rm -f $(DESTDIR)@libexecdir@/listservices.html
rm -f $(DESTDIR)@libexecdir@/listsystemsettings.html
rm -f $(DESTDIR)@libexecdir@/locklog.html
rm -f $(DESTDIR)@libexecdir@/postpodcast.html
rm -f $(DESTDIR)@libexecdir@/postrss.html
rm -f $(DESTDIR)@libexecdir@/rehash.html
rm -f $(DESTDIR)@libexecdir@/removecart.html
rm -f $(DESTDIR)@libexecdir@/removecut.html
rm -f $(DESTDIR)@libexecdir@/removepodcast.html
rm -f $(DESTDIR)@libexecdir@/removepodrss.html
rm -f $(DESTDIR)@libexecdir@/savefile.html
rm -f $(DESTDIR)@libexecdir@/savelog.html
rm -f $(DESTDIR)@libexecdir@/savepodcast.html
@ -140,9 +148,13 @@ EXTRA_DIST = addcart.html\
listservices.html\
listsystemsettings.html\
locklog.html\
postpodcast.html\
postrss.html\
rehash.html\
removecart.html\
removecut.html\
removepodcast.html\
removerss.html\
savefile.html\
savelog.html\
savepodcast.html\

View File

@ -0,0 +1,33 @@
<html>
<head>
<title>Rivendell POSTPODCAST Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="40">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">ID:</td>
<td><input type="text" name="ID" size="12" maxlength="12"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>

33
web/tests/postrss.html Normal file
View File

@ -0,0 +1,33 @@
<html>
<head>
<title>Rivendell POSTRSS Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="42">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">ID:</td>
<td><input type="text" name="ID" size="12" maxlength="12"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>

View File

@ -0,0 +1,33 @@
<html>
<head>
<title>Rivendell REMOVEPODCAST Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="41">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">ID:</td>
<td><input type="text" name="ID" size="12" maxlength="12"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>

33
web/tests/removerss.html Normal file
View File

@ -0,0 +1,33 @@
<html>
<head>
<title>Rivendell REMOVERSS Service Test Harness</title>
<body>
<form action="/rd-bin/rdxport.cgi" method="post" enctype="multipart/form-data">
<input type="hidden" name="COMMAND" value="43">
<table cellpadding="0" cellspacing="2" border="0">
<tr>
<td align="right">LOGIN NAME:</td>
<td><input type="text" name="LOGIN_NAME" size="20" maxlength="255"></td>
</tr>
<tr>
<td align="right">PASSWORD:</td>
<td><input type="password" name="PASSWORD" size="20" maxlength="32"></td>
</tr>
<tr>
<td align="right">TICKET:</td>
<td><input type="text" name="TICKET" size="40" maxlength="40"></td>
</tr>
<tr>
<td align="right">ID:</td>
<td><input type="text" name="ID" size="12" maxlength="12"></td>
</tr>
<tr>
<td colspan="2" align="right">&nbsp;</td>
</tr>
<tr>
<td colspan="2" align="right"><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>