From 6516c20ff6e19a3914710b27743f94ead5cd9591 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Thu, 17 Sep 2020 16:19:26 -0400 Subject: [PATCH] 2020-09-17 Fred Gleason * Added an 'RDUser::feedAuthorized()' method. * Added a 'SavePodcast' method to the Web API. * Added a 'GetPodcast' method to the Web API. * Added a 'DeletePodcast' method to the Web API. Signed-off-by: Fred Gleason --- ChangeLog | 5 + docs/apis/web_api.xml | 200 ++++++++++++++++++++++++++++++++++ lib/rduser.cpp | 16 +++ lib/rduser.h | 1 + lib/rdxport_interface.h | 3 + web/rdxport/Makefile.am | 1 + web/rdxport/podcasts.cpp | 204 +++++++++++++++++++++++++++++++++++ web/rdxport/rdxport.cpp | 15 ++- web/rdxport/rdxport.h | 3 + web/tests/Makefile.am | 12 ++- web/tests/deletepodcast.html | 33 ++++++ web/tests/getpodcast.html | 33 ++++++ web/tests/savepodcast.html | 36 +++++++ 13 files changed, 559 insertions(+), 3 deletions(-) create mode 100644 web/rdxport/podcasts.cpp create mode 100644 web/tests/deletepodcast.html create mode 100644 web/tests/getpodcast.html create mode 100644 web/tests/savepodcast.html diff --git a/ChangeLog b/ChangeLog index 1dd0ca69..6f77a2ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20280,3 +20280,8 @@ 2020-08-28 Fred Gleason * Added an 'RDNotification::FeedType' value to the 'RDNotification::Type' enumeration. +2020-09-17 Fred Gleason + * Added an 'RDUser::feedAuthorized()' method. + * Added a 'SavePodcast' method to the Web API. + * Added a 'GetPodcast' method to the Web API. + * Added a 'DeletePodcast' method to the Web API. diff --git a/docs/apis/web_api.xml b/docs/apis/web_api.xml index 3970ee76..24f5fe3c 100644 --- a/docs/apis/web_api.xml +++ b/docs/apis/web_api.xml @@ -831,6 +831,68 @@ + + DeletePodcast + Delete posted podcast audio from the audio store + + Command Code: RDXPORT_COMMAND_DELETE_PODCAST + + + Required User Permissions: Delete Podcast, Feed Permission + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + GetPodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 39 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ EditCart @@ -1717,6 +1779,68 @@ + + GetPodcast + Get posted podcast audio + + Command Code: RDXPORT_COMMAND_GET_PODCAST + + + Required User Permissions: Edit Podcast, Feed Permission + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + GetPodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 37 + + + Mandatory + + + + + ID + + + ID of podcast item (integer) + + + Mandatory + + + + +
+
+ Import Import audio data into the audio store @@ -3526,6 +3650,82 @@ + + SavePodcast + Save posted podcast audio + + Command Code: RDXPORT_COMMAND_SAVE_PODCAST + + + Required User Permissions: Create Podcast, Feed Permission + + + A 404 error will be returned if the + requested podcast's parent feed is not authorized for the specified + Rivendell user in RDAdmin->ManageUsers->PodcastFeedPermissions. + + + NOTE: The method must be called with 'multipart/form-data' encoding. + + + SavePodcast Call Fields + + + + + + + + FIELD NAME + + + MEANING + + + REMARKS + + + + + + + COMMAND + + + 38 + + + Mandatory + + + + + ID + + + ID of podcast (integer) + + + Mandatory + + + + + FILENAME + + + Binary file data + + + Mandatory + + + + +
+
+ SaveString diff --git a/lib/rduser.cpp b/lib/rduser.cpp index 1f901c00..cc87f977 100644 --- a/lib/rduser.cpp +++ b/lib/rduser.cpp @@ -544,6 +544,22 @@ bool RDUser::cartAuthorized(unsigned cartnum) const } +bool RDUser::feedAuthorized(const QString &keyname) +{ + QString sql; + RDSqlQuery *q; + bool ret=false; + + sql=QString("select ID from FEED_PERMS where ")+ + "(USER_NAME=\""+RDEscapeString(user_name)+"\")&&"+ + "(KEY_NAME=\""+RDEscapeString(keyname)+"\")"; + q=new RDSqlQuery(sql); + ret=q->first(); + delete q; + return ret; +} + + QString RDUser::serviceCheckDefault(QString serv) const { bool match_flag = false; diff --git a/lib/rduser.h b/lib/rduser.h index 24ab4261..99d95c92 100644 --- a/lib/rduser.h +++ b/lib/rduser.h @@ -93,6 +93,7 @@ class RDUser bool groupAuthorized(const QString &group_name); QStringList groups() const; bool cartAuthorized(unsigned cartnum) const; + bool feedAuthorized(const QString &keyname); QString serviceCheckDefault(QString serv) const; QStringList services() const; static bool emailIsValid(const QString &addr); diff --git a/lib/rdxport_interface.h b/lib/rdxport_interface.h index 051b04a2..0344dbcd 100644 --- a/lib/rdxport_interface.h +++ b/lib/rdxport_interface.h @@ -57,6 +57,9 @@ #define RDXPORT_COMMAND_LOCKLOG 34 #define RDXPORT_COMMAND_SAVESTRING 35 #define RDXPORT_COMMAND_SAVEFILE 36 +#define RDXPORT_COMMAND_GET_PODCAST 37 +#define RDXPORT_COMMAND_SAVE_PODCAST 38 +#define RDXPORT_COMMAND_DELETE_PODCAST 39 #endif // RDXPORT_INTERFACE_H diff --git a/web/rdxport/Makefile.am b/web/rdxport/Makefile.am index 586aec3d..442d6818 100644 --- a/web/rdxport/Makefile.am +++ b/web/rdxport/Makefile.am @@ -40,6 +40,7 @@ dist_rdxport_cgi_SOURCES = audioinfo.cpp\ exportpeaks.cpp\ import.cpp\ logs.cpp\ + podcasts.cpp\ rdxport.cpp rdxport.h\ rehash.cpp\ tests.cpp\ diff --git a/web/rdxport/podcasts.cpp b/web/rdxport/podcasts.cpp new file mode 100644 index 00000000..7f36f4b8 --- /dev/null +++ b/web/rdxport/podcasts.cpp @@ -0,0 +1,204 @@ +// podcasts.cpp +// +// Rivendell web service portal -- Podcast services +// +// (C) Copyright 2010-2020 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 "rdxport.h" + +void Xport::SavePodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + RDFeed *feed=NULL; + QString filename; + QString msg="OK"; + + if(!xport_post->getValue("ID",&cast_id)) { + XmlExit("Missing ID",400,"podcasts.cpp",LINE_NUMBER); + } + + if(!xport_post->getValue("FILENAME",&filename)) { + XmlExit("Missing FILENAME",400,"podcasts.cpp",LINE_NUMBER); + } + if(!xport_post->isFile("FILENAME")) { + XmlExit("Missing file data",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))) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + feed=new RDFeed(keyname,rda->config(),this); + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if(!RDCopy(filename,destpath)) { + delete feed; + delete cast; + XmlExit("Internal server error [copy failed]",500,"podcasts.cpp", + LINE_NUMBER); + } + if(chmod(destpath.toUtf8(),S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)!=0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + unlink(destpath.toUtf8()); + delete feed; + delete cast; + XmlExit(err_msg.toUtf8(),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,"saved podcast \"%s\"",destpath.toUtf8().constData()); + + delete feed; + delete cast; + + Exit(0); +} + + +void Xport::GetPodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + QString msg="OK"; + int fd=-1; + struct stat st; + ssize_t n=0; + char *data=NULL; + + 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()->editPodcast())||(!rda->user()->feedAuthorized(keyname))) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if((fd=open(destpath.toUtf8(),O_RDONLY))<0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + memset(&st,0,sizeof(st)); + if(fstat(fd,&st)!=0) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),500,"podcasts.cpp",LINE_NUMBER); + } + + printf("Content-type: audio/x-mpeg\n"); + printf("Content-length: %ld\n",st.st_size); + printf("\n"); + fflush(stdout); + data=new char[st.st_blksize]; + n=read(fd,data,st.st_blksize); + while(n>0) { + write(1,data,n); + n=read(fd,data,st.st_blksize); + } + delete data; + close(fd); + + rda->syslog(LOG_DEBUG,"served podcast \"%s\"",destpath.toUtf8().constData()); + + delete cast; + + Exit(0); +} + + +void Xport::DeletePodcast() +{ + int cast_id=0; + QString keyname; + QString destpath; + QString err_msg; + RDPodcast *cast=NULL; + QString msg="OK"; + + 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))) { + delete cast; + XmlExit("No such podcast",404,"podcasts.cpp",LINE_NUMBER); + } + destpath=QString(RD_AUDIO_ROOT)+"/"+cast->audioFilename(); + + if(unlink(destpath.toUtf8())!=0) { + if(errno!=ENOENT) { + err_msg=QString().sprintf("Internal server error [%s]",strerror(errno)); + delete cast; + XmlExit(err_msg.toUtf8(),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,"deleted podcast \"%s\"",destpath.toUtf8().constData()); + + delete cast; + + Exit(0); +} diff --git a/web/rdxport/rdxport.cpp b/web/rdxport/rdxport.cpp index bb5f6aaa..724292c9 100644 --- a/web/rdxport/rdxport.cpp +++ b/web/rdxport/rdxport.cpp @@ -2,7 +2,7 @@ // // Rivendell web service portal // -// (C) Copyright 2010-2019 Fred Gleason +// (C) Copyright 2010-2020 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 @@ -154,6 +154,7 @@ void Xport::ripcConnectedData(bool state) // Read Command Variable and Dispatch // int command=xport_post->value("COMMAND").toInt(); + switch(command) { case RDXPORT_COMMAND_EXPORT: Export(); @@ -295,6 +296,18 @@ void Xport::ripcConnectedData(bool state) SaveFile(); break; + case RDXPORT_COMMAND_SAVE_PODCAST: + SavePodcast(); + break; + + case RDXPORT_COMMAND_GET_PODCAST: + GetPodcast(); + break; + + case RDXPORT_COMMAND_DELETE_PODCAST: + DeletePodcast(); + break; + default: printf("Content-type: text/html\n\n"); printf("rdxport: missing/invalid command\n"); diff --git a/web/rdxport/rdxport.h b/web/rdxport/rdxport.h index bc8ccbd1..6098bc96 100644 --- a/web/rdxport/rdxport.h +++ b/web/rdxport/rdxport.h @@ -82,6 +82,9 @@ class Xport : public QObject void ListCartSchedCodes(); void ListServices(); void ListSystemSettings(); + void SavePodcast(); + void GetPodcast(); + void DeletePodcast(); void LockLog(); QString LogLockXml(bool result,const QString &log_name,const QString &guid, const QString &username,const QString &stationname, diff --git a/web/tests/Makefile.am b/web/tests/Makefile.am index 61a8372f..4b080b9a 100644 --- a/web/tests/Makefile.am +++ b/web/tests/Makefile.am @@ -2,7 +2,7 @@ ## ## Automake.am for rivendell/web/tests ## -## (C) Copyright 2010,2013-2015 Fred Gleason +## (C) Copyright 2010-2020 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 @@ -31,13 +31,14 @@ install-exec-am: cp createticket.html $(DESTDIR)@libexecdir@ cp delete_audio.html $(DESTDIR)@libexecdir@ cp deletelog.html $(DESTDIR)@libexecdir@ + cp deletepodcast.html $(DESTDIR)@libexecdir@ cp editcart.html $(DESTDIR)@libexecdir@ cp editcut.js $(DESTDIR)@libexecdir@ cp editcut.html $(DESTDIR)@libexecdir@ cp editcart.js $(DESTDIR)@libexecdir@ cp export.html $(DESTDIR)@libexecdir@ cp exportpeaks.html $(DESTDIR)@libexecdir@ - cp import.html $(DESTDIR)@libexecdir@ + cp getpodcast.html $(DESTDIR)@libexecdir@ cp import.html $(DESTDIR)@libexecdir@ cp listcart.html $(DESTDIR)@libexecdir@ cp listcarts.html $(DESTDIR)@libexecdir@ @@ -57,6 +58,7 @@ install-exec-am: cp removecut.html $(DESTDIR)@libexecdir@ cp savefile.html $(DESTDIR)@libexecdir@ cp savelog.html $(DESTDIR)@libexecdir@ + cp savepodcast.html $(DESTDIR)@libexecdir@ cp savestring.html $(DESTDIR)@libexecdir@ cp trimaudio.html $(DESTDIR)@libexecdir@ cp unassignschedcode.html $(DESTDIR)@libexecdir@ @@ -73,12 +75,14 @@ uninstall-local: rm -f $(DESTDIR)@libexecdir@/createticket.html rm -f $(DESTDIR)@libexecdir@/delete_audio.html rm -f $(DESTDIR)@libexecdir@/deletelog.html + rm -f $(DESTDIR)@libexecdir@/deletepodcast.html rm -f $(DESTDIR)@libexecdir@/editcart.html rm -f $(DESTDIR)@libexecdir@/editcart.js rm -f $(DESTDIR)@libexecdir@/editcut.html rm -f $(DESTDIR)@libexecdir@/editcut.js rm -f $(DESTDIR)@libexecdir@/export.html rm -f $(DESTDIR)@libexecdir@/exportpeaks.html + rm -f $(DESTDIR)@libexecdir@/getpodcast.html rm -f $(DESTDIR)@libexecdir@/import.html rm -f $(DESTDIR)@libexecdir@/listcart.html rm -f $(DESTDIR)@libexecdir@/listcarts.html @@ -98,6 +102,7 @@ uninstall-local: rm -f $(DESTDIR)@libexecdir@/removecut.html rm -f $(DESTDIR)@libexecdir@/savefile.html rm -f $(DESTDIR)@libexecdir@/savelog.html + rm -f $(DESTDIR)@libexecdir@/savepodcast.html rm -f $(DESTDIR)@libexecdir@/savestring.html rm -f $(DESTDIR)@libexecdir@/trimaudio.html rm -f $(DESTDIR)@libexecdir@/unassignschedcode.html @@ -113,12 +118,14 @@ EXTRA_DIST = addcart.html\ createticket.html\ delete_audio.html\ deletelog.html\ + deletepodcast.html\ editcart.html\ editcart.js\ editcut.html\ editcut.js\ export.html\ exportpeaks.html\ + getpodcast.html\ import.html\ listcart.html\ listcarts.html\ @@ -138,6 +145,7 @@ EXTRA_DIST = addcart.html\ removecut.html\ savefile.html\ savelog.html\ + savepodcast.html\ savestring.html\ trimaudio.html\ unassignschedcode.html\ diff --git a/web/tests/deletepodcast.html b/web/tests/deletepodcast.html new file mode 100644 index 00000000..2f7ecb52 --- /dev/null +++ b/web/tests/deletepodcast.html @@ -0,0 +1,33 @@ + + +Rivendell DELETEPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/getpodcast.html b/web/tests/getpodcast.html new file mode 100644 index 00000000..fc64032d --- /dev/null +++ b/web/tests/getpodcast.html @@ -0,0 +1,33 @@ + + +Rivendell GETPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
 
+
+ + diff --git a/web/tests/savepodcast.html b/web/tests/savepodcast.html new file mode 100644 index 00000000..6c827b04 --- /dev/null +++ b/web/tests/savepodcast.html @@ -0,0 +1,36 @@ + + +Rivendel SAVEPODCAST Service Test Harness + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
TICKET:
ID:
FILENAME:
 
+
+ +