Fred Gleason fec324abd8 2018-05-31 Fred Gleason <fredg@paravelsystems.com>
* Fixed a bug that broke the build on RHEL 6.
2018-05-31 17:12:46 -04:00

364 lines
8.7 KiB
C++

// webget.cpp
//
// Rivendell download utility
//
// (C) Copyright 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/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <qapplication.h>
#include <rdapplication.h>
#include <rdescape_string.h>
#include <rdweb.h>
#include "webget.h"
MainObject::MainObject(QObject *parent)
:QObject(parent)
{
QString err_msg;
//
// Open the Database
//
rda=new RDApplication("webget.cgi","webget.cgi",WEBGET_CGI_USAGE,this);
if(!rda->open(&err_msg)) {
XmlExit(err_msg,500,"webget.cpp",LINE_NUMBER);
}
//
// Read Command Options
//
for(unsigned i=0;i<rda->cmdSwitch()->keys();i++) {
if(!rda->cmdSwitch()->processed(i)) {
XmlExit("unknown command option",500,"webget.cpp",LINE_NUMBER);
}
}
//
// Drop root permissions
//
if(setgid(rda->config()->gid())<0) {
XmlExit("Unable to set Rivendell group",500,"webget.cpp",LINE_NUMBER);
}
if(setuid(rda->config()->uid())<0) {
XmlExit("Unable to set Rivendell user",500,"webget.cpp",LINE_NUMBER);
}
if(getuid()==0) {
XmlExit("Rivendell user should never be \"root\"!",500,"webget.cpp",
LINE_NUMBER);
}
//
// Determine Connection Type
//
if(getenv("REQUEST_METHOD")==NULL) {
XmlExit("missing REQUEST_METHOD",500,"webget.cpp",LINE_NUMBER);
}
if(QString(getenv("REQUEST_METHOD")).lower()!="post") {
XmlExit("invalid web method",500,"webget.cpp",LINE_NUMBER);
}
if(getenv("REMOTE_ADDR")!=NULL) {
webget_remote_address.setAddress(getenv("REMOTE_ADDR"));
}
if(getenv("REMOTE_HOST")!=NULL) {
webget_remote_hostname=getenv("REMOTE_HOST");
}
if(webget_remote_hostname.isEmpty()) {
webget_remote_hostname=webget_remote_address.toString();
}
//
// Generate Post
//
webget_post=new RDFormPost(RDFormPost::AutoEncoded,false);
if(webget_post->error()!=RDFormPost::ErrorOk) {
XmlExit(webget_post->errorString(webget_post->error()),400,"webget.cpp",
LINE_NUMBER);
Exit(0);
}
//
// Authenticate Connection
//
if(!Authenticate()) {
printf("Content-type: text/html\n");
printf("Status: 400\n");
printf("\n");
printf("Invalid User name or Password!\n");
Exit(0);
}
//
// Connect to ripcd(8)
//
connect(rda->ripc(),SIGNAL(connected(bool)),
this,SLOT(ripcConnectedData(bool)));
rda->ripc()->
connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password());
}
void MainObject::ripcConnectedData(bool state)
{
if(!state) {
XmlExit("unable to connect to ripc service",500,"webget.cpp",LINE_NUMBER);
Exit(0);
}
QString title;
if(!webget_post->getValue("title",&title)) {
XmlExit("missing \"title\"",400,"webget.cpp",LINE_NUMBER);
}
int samprate;
if(!webget_post->getValue("samprate",&samprate)) {
XmlExit("missing \"samprate\"",400,"webget.cpp",LINE_NUMBER);
}
int format;
if(!webget_post->getValue("format",&format)) {
XmlExit("missing \"format\"",400,"webget.cpp",LINE_NUMBER);
}
int bitrate=0;
if((format==RDSettings::MpegL2)||(format==RDSettings::MpegL3)) {
if(!webget_post->getValue("bitrate",&bitrate)) {
XmlExit("missing \"bitrate\"",400,"webget.cpp",LINE_NUMBER);
}
}
int quality=0;
if(format==RDSettings::OggVorbis) {
if(!webget_post->getValue("quality",&quality)) {
XmlExit("missing \"quality\"",400,"webget.cpp",LINE_NUMBER);
}
}
unsigned cartnum=0;
int cutnum=0;
QString sql=QString("select ")+
"CUTS.CUT_NAME from "+
"CART left join CUTS on CART.NUMBER=CUTS.CART_NUMBER where "+
"CART.TITLE=\""+RDEscapeString(title)+"\" && "+
QString().sprintf("CART.TYPE=%d",RDCart::Audio);
RDSqlQuery *q=new RDSqlQuery(sql);
if(q->first()) {
cartnum=RDCut::cartNumber(q->value(0).toString());
cutnum=RDCut::cutNumber(q->value(0).toString());
}
delete q;
if(cartnum==0) {
printf("Content-type: text/html\n");
printf("Status: 400\n");
printf("\n");
printf("No such cart!\n");
Exit(0);
}
//
// Audio Settings
//
RDSettings *settings=new RDSettings();
settings->setFormat((RDSettings::Format)format);
settings->setChannels(1);
settings->setSampleRate(samprate);
settings->setBitRate(bitrate);
settings->setQuality(quality);
settings->setNormalizationLevel(-100);
//
// Generate Metadata
//
RDCart *cart=new RDCart(cartnum);
RDCut *cut=new RDCut(cartnum,cutnum);
RDWaveData *wavedata=NULL;
QString rdxl;
float speed_ratio=1.0;
wavedata=new RDWaveData();
if(wavedata!=NULL) {
cart->getMetadata(wavedata);
cut->getMetadata(wavedata);
if(cart->enforceLength()) {
speed_ratio=(float)cut->length()/(float)cart->forcedLength();
}
rdxl=cart->xml(true,false,settings,cutnum);
}
//
// Export Cut
//
int fd;
ssize_t n;
uint8_t data[2048];
QString err_msg;
RDTempDirectory *tempdir=new RDTempDirectory("rdxport-export");
if(!tempdir->create(&err_msg)) {
XmlExit("unable to create temporary directory ["+err_msg+"]",500);
}
QString tmpfile=tempdir->path()+"/exported_audio";
RDAudioConvert *conv=new RDAudioConvert(this);
conv->setSourceFile(RDCut::pathName(cartnum,cutnum));
conv->setDestinationFile(tmpfile);
conv->setDestinationSettings(settings);
conv->setDestinationWaveData(wavedata);
conv->setDestinationRdxl(rdxl);
conv->setRange(cut->startPoint(),cut->endPoint());
conv->setSpeedRatio(speed_ratio);
delete cut;
delete cart;
RDAudioConvert::ErrorCode conv_err;
int resp_code=0;
switch(conv_err=conv->convert()) {
case RDAudioConvert::ErrorOk:
switch(settings->format()) {
case RDSettings::Pcm16:
case RDSettings::Pcm24:
printf("Content-type: audio/x-wav\n\n");
break;
case RDSettings::MpegL1:
case RDSettings::MpegL2:
case RDSettings::MpegL2Wav:
case RDSettings::MpegL3:
printf("Content-type: audio/x-mpeg\n\n");
break;
case RDSettings::OggVorbis:
printf("Content-type: audio/ogg\n\n");
break;
case RDSettings::Flac:
printf("Content-type: audio/flac\n\n");
break;
}
fflush(NULL);
if((fd=open(tmpfile,O_RDONLY))>=0) {
while((n=read(fd,data,2048))>0) {
write(1,data,n);
}
}
close(fd);
unlink(tmpfile);
delete tempdir;
Exit(0);
break;
case RDAudioConvert::ErrorFormatNotSupported:
case RDAudioConvert::ErrorInvalidSettings:
resp_code=415;
break;
case RDAudioConvert::ErrorNoSource:
resp_code=404;
break;
case RDAudioConvert::ErrorNoDestination:
case RDAudioConvert::ErrorInvalidSource:
case RDAudioConvert::ErrorNoSpace:
case RDAudioConvert::ErrorInternal:
case RDAudioConvert::ErrorNoDisc:
case RDAudioConvert::ErrorNoTrack:
case RDAudioConvert::ErrorInvalidSpeed:
case RDAudioConvert::ErrorFormatError:
resp_code=500;
break;
}
delete conv;
delete settings;
if(wavedata!=NULL) {
delete wavedata;
}
delete tempdir;
if(resp_code==200) {
Exit(200);
}
else {
XmlExit(RDAudioConvert::errorText(conv_err),resp_code,"export.cpp",
LINE_NUMBER,conv_err);
}
}
bool MainObject::Authenticate()
{
QString name;
QString passwd;
if(!webget_post->getValue("LOGIN_NAME",&name)) {
return false;
}
if(!webget_post->getValue("PASSWORD",&passwd)) {
return false;
}
RDUser *user=new RDUser(name);
if((!user->exists())||
(!user->checkPassword(passwd,false))||
(!user->webgetLogin())) {
return false;
}
return true;
}
void MainObject::Exit(int code)
{
if(webget_post!=NULL) {
delete webget_post;
}
exit(code);
}
void MainObject::XmlExit(const QString &str,int code,const QString &srcfile,
int srcline,RDAudioConvert::ErrorCode err)
{
if(webget_post!=NULL) {
delete webget_post;
}
#ifdef RDXPORT_DEBUG
if(srcline>0) {
RDXMLResult(str+" \""+srcfile+"\" "+QString().sprintf("line %d",srcline),
code,err);
}
else {
RDXMLResult(str,code,err);
}
#else
RDXMLResult(str,code,err);
#endif // RDXPORT_DEBUG
exit(0);
}
int main(int argc,char *argv[])
{
QApplication a(argc,argv,false);
new MainObject();
return a.exec();
}