// rdweb.cpp // // Functions for interfacing with web components using the // Common Gateway Interface (CGI) Standard // // (C) Copyright 1996-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 "rdconf.h" #include "rddatetime.h" #include "rddb.h" #include "rdescape_string.h" #include "rdtempdirectory.h" #include "rduser.h" #include "rdwebresult.h" #include "rdweb.h" /* RDReadPost(char *cBuffer,int dSize) */ /* This function reads POST data (such as that submitted by an HTML form) into the buffer pointed to by cBuffer. The size of the buffer is indicated by dSize. RETURNS: Number of bytes read if the function is successful -1 if an error is encountered. */ int RDReadPost(char *cBuffer,int dSize) { int dPostSize=0; if(strcasecmp(getenv("REQUEST_METHOD"),"POST")!=0) { /* No post data to receive! */ return -1; } sscanf(getenv("CONTENT_LENGTH"),"%d",&dPostSize); if(dPostSize>=dSize) { /* Data block too large! */ return -1; } dPostSize++; fgets(cBuffer,dPostSize,stdin); return dPostSize; } /* * int RDPutPostString(char *sPost,char *sArg,char *sValue,int dMaxSize) * * This function changes the contents of the POST buffer pointed to by * 'sPost'. If the entry pointed to by 'sArg' exists, it's value is * replaced by the string pointed to by 'sValue'. If the entry doesn't * exist, it is created. 'dMaxSize' is the maximum allowable size of 'sPost'. * * RETURNS: If successful, a pointer to the start of the updated value * If unsuccessful, -1 */ int RDPutPostString(char *sPost,char *sArg,char *sValue,int dMaxSize) { int dOrigin; /* Start of insert point */ int dValue; /* Length of sValue */ int i; /* General purpose counter */ char sAccum[CGI_ACCUM_SIZE]; /* * Does the argument already exist? */ dOrigin=RDFindPostString(sPost,sArg,sAccum,CGI_ACCUM_SIZE); if(dOrigin<0) { /* * Create a new entry * Will it fit? */ dOrigin=strlen(sPost); if((dOrigin+strlen(sArg)+strlen(sValue)+2)>=(unsigned)dMaxSize) { return -1; } /* * Append to the end */ strcat(sPost,"&"); strcat(sPost,sArg); strcat(sPost,"="); dOrigin=strlen(sPost); strcat(sPost,sValue); } else { /* * The argument exists, so update it */ dValue=strlen(sValue); if(RDBufferDiff(sPost,dOrigin,dValue-strlen(sAccum),dMaxSize)<0) { return -1; } for(i=0;i'9') && (sString[i]<'A')) || ((sString[i]>'Z') && (sString[i]<'a')) || (sString[i]>'z'))) { if(RDBufferDiff(sString,i,2,dMaxSize)<0) { return -1; } sprintf(sAccum,"%%%2x",sString[i]); sString[i++]=sAccum[0]; sString[i++]=sAccum[1]; sString[i]=sAccum[2]; } if(sString[i]==' ') { sString[i]='+'; } i++; } return strlen(sString); } /* * int RDEncodeSQLString(char *sString,int dMaxSize) * * This function processes the string pointed to by 'sString', * escaping the ' \ and " characters. * * RETURNS: If successful, the new size of 'sString' * If unsuccessful, -1 */ int RDEncodeSQLString(char *sString,int dMaxSize) { int i; /* General Purpose Counter */ char sAccum[4]; /* General String Buffer */ i=0; while(sString[i]!=0) { if((sString[i]=='%')||(sString[i]==34)||(sString[i]==39)) { if(RDBufferDiff(sString,i,2,dMaxSize)<0) { return -1; } sprintf(sAccum,"%%%2x",sString[i]); sString[i++]=sAccum[0]; sString[i++]=sAccum[1]; sString[i]=sAccum[2]; } i++; } return strlen(sString); } int RDDecodeString(char *sString) { int i=0,j=0,k; char sAccum[4]; while(sString[i]!=0) { switch(sString[i]) { case '+': sString[j]=' '; break; case '%': /* escape sequence */ sAccum[0]=sString[++i]; sAccum[1]=sString[++i]; sAccum[2]=0; sscanf(sAccum,"%x",&k); sString[j]=(char)k; break; default: sString[j]=sString[i]; break; } i++; j++; } sString[j]=0; return --j; } /* * RDPutPlaintext(char *sPost,int dMaxSize) * * This function appends a block of text consisting of the *decoded* values * of all the POST values found in the buffer pointed to by 'sPost' into * the buffer pointed to by 'sPost'. The block is enclosed by the HTML * start and end comment sequence (). 'sPost' is of maximum size * 'dMaxSize'. * * RETURNS: If successful, the new size of 'sPost'. * If unsuccessful, -1. */ int RDPutPlaintext(char *sPost,int dMaxSize) { int dOriginalsize=0,dPostsize=0; /* Current post buffer length */ int i,j=0; /* General purpose counter */ int iState=0; /* State Counter */ char sAccum[CGI_ACCUM_SIZE]; /* General String Buffer */ int dAccum; /* Length of sAccum */ /* * Initialize some data structures */ dOriginalsize=strlen(sPost); dPostsize=dOriginalsize; /* * Append the start of comment sequence */ if((dPostsize+3)>=dMaxSize) { return -1; } strcat(sPost,"&< "); dPostsize+=3; /* * Scan for value strings */ for(i=0;i=dMaxSize) { return -1; } strcat(sPost,sAccum); dPostsize+=dAccum; iState=0; break; default: /* Another character in value string */ if((sPost[i]!='<') && (sPost[i]!='>')) { sAccum[j++]=sPost[i]; } break; } case 10: /* Middle of a comment */ switch(sPost[i]) { case '>': /* End of comment */ iState=0; break; } break; default: /* Parser error! */ return -1; break; } } /* * Append the end of comment sequence */ if((dPostsize+1)>=dMaxSize) { return -1; } strcat(sPost,">"); dPostsize+=1; return dPostsize; } /* * int RDPurgePlaintext(char *sPost,int dMaxSize) * * This function removes one or more plaintext blocks enclosed by HTML comment * sequences () from the buffer pointed to by 'sPost', of * maximum size 'dMaxSize'. * * RETURNS: If successful, the new size of 'sPost'. * If unsuccessful, -1 */ int RDPurgePlaintext(char *sPost,int dMaxSize) { int i=0; /* General Purpose Counters */ int dComments=0; /* Comment State Switch */ int dStart=0; /* Comment Startpoint Pointer */ /* * Scan for comment sequences */ while(sPost[i]!=0) { if((sPost[i]=='<') && (dComments==0)) { /* Start of comment */ dStart=i; dComments=1; } if((sPost[i]=='>') && (dComments==1)) { /* End of comment */ if(RDBufferDiff(sPost,dStart,dStart-i-1,dMaxSize)<0) { return -1; } if(sPost[i]==0) { /* Ensure a proper exit if at end of string */ i--; } } i++; } /* * Clean up and exit nicely */ RDPruneAmp(sPost); return strlen(sPost); } void RDCgiError(const char *str,int resp_code) { /* The cgi header */ printf("Content-type: text/html\n"); printf("Status: %d\n",resp_code); printf("\n"); /* The html header */ printf("\n"); printf("\n"); printf(""); printf("CGI Internal Error %d",resp_code); printf("\n"); printf("\n"); /* The body of the message */ printf("

Oops!


\n"); printf("We seem to have encountered a problem! The system says:
\n"); printf("
%d
%s

\n",resp_code,str); /* The html footer */ printf("\n"); exit(0); } extern void RDXMLResult(const char *str,int resp_code, RDAudioConvert::ErrorCode err) { RDWebResult *we=new RDWebResult(str,resp_code,err); printf("Content-type: application/xml\n"); printf("Status: %d\n",resp_code); printf("\n"); printf("%s",(const char *)we->xml()); delete we; exit(0); } /* * int BufferDiff(char sString,int dOrigin,int dDiff,int dMaxSize) * * This function adds (+ value) or deletes (- value) 'dDiff' characters * from the string pointed to by 'sString' at the offset location pointed * to by 'dOrigin'. 'dMaxSize' is the maximum allowable size of 'sString'. * * RETURNS: If successful, the new size of 'sString' * If unsuccessful, -1 */ int RDBufferDiff(char *sString,int dOrigin,int dDiff,int dMaxSize) { int dOldSize,dNewSize; int i; /* * Will it fit? */ dOldSize=strlen(sString); if((dOldSize+dDiff)>=dMaxSize) { return -1; } dNewSize=dOldSize+dDiff; /* * Adding characters */ if(dDiff>0) { for(i=dOldSize;i>dOrigin;i--) { sString[i+dDiff]=sString[i]; } return dNewSize; } /* * No Change */ if(dDiff==0) { return dNewSize; } /* * Deleting Characters */ if(dDiff<0) { for(i=dOrigin;imaxlen) { dest[j]=0; return j; } dest[j]=0; strcat(dest,"""); i++; j+=6; } else { if((j+2)>maxlen) { dest[j]=0; return j; } dest[j++]=src[i++]; } } dest[j]=0; return j; } long int RDAuthenticateLogin(const QString &username,const QString &passwd, const QHostAddress &addr) { // // Authenticate User // RDUser *user=new RDUser(username); if(!user->exists()) { delete user; return -1; } if(!user->checkPassword(passwd,true)) { delete user; return -1; } delete user; // // Create Session // time_t timeval; timeval=time(&timeval); srandom(timeval); long int session=random(); QString sql=QString("insert into WEB_CONNECTIONS set ")+ QString().sprintf("SESSION_ID=%ld,",session)+ "LOGIN_NAME=\""+RDEscapeString(username)+"\","+ "IP_ADDRESS=\""+addr.toString()+"\","+ "TIME_STAMP=now()"; RDSqlQuery *q=new RDSqlQuery(sql); delete q; return session; } QString RDAuthenticateSession(long int session_id,const QHostAddress &addr) { QString sql; RDSqlQuery *q; // // Expire Stale Sessions // QDateTime current_datetime= QDateTime(QDate::currentDate(),QTime::currentTime()); sql=QString("delete from WEB_CONNECTIONS where ")+ "TIME_STAMP<\""+current_datetime.addSecs(-RD_WEB_SESSION_TIMEOUT). toString("yyyy-MM-dd hh:mm:ss")+"\""; q=new RDSqlQuery(sql); delete q; // // Check for Session // sql=QString("select LOGIN_NAME,IP_ADDRESS from WEB_CONNECTIONS where ")+ QString().sprintf("SESSION_ID=%ld",session_id); q=new RDSqlQuery(sql); if(!q->first()) { delete q; return QString(); } if(q->value(1).toString()!=addr.toString()) { delete q; return QString(); } QString name=q->value(0).toString(); delete q; // // Update Session // sql=QString("update WEB_CONNECTIONS set ")+ "TIME_STAMP=\""+current_datetime.toString("yyyy-MM-dd hh:mm:dd")+"\" "+ QString().sprintf("where SESSION_ID=%ld",session_id); q=new RDSqlQuery(sql); delete q; return name; } void RDLogoutSession(long int session_id,const QHostAddress &addr) { QString sql=QString().sprintf("select IP_ADDRESS from WEB_CONNECTIONS \ where SESSION_ID=%ld", session_id); RDSqlQuery *q=new RDSqlQuery(sql); if(!q->first()) { delete q; return; } if(q->value(0).toString()!=addr.toString()) { delete q; return; } delete q; sql=QString().sprintf("delete from WEB_CONNECTIONS where SESSION_ID=%ld", session_id); q=new RDSqlQuery(sql); delete q; } bool RDParsePost(std::map *vars) { std::map headers; bool header=true; FILE *f=NULL; char *data=NULL; ssize_t n=0; QString sep; QString name; QString filename; QString tempdir; int fd=-1; // // Initialize Temp Directory Path // tempdir=RDTempDirectory::basePath()+"/rivendellXXXXXX"; // // Get message part separator // if(getenv("REQUEST_METHOD")==NULL) { return false; } if(QString(getenv("REQUEST_METHOD")).lower()!="post") { return false; } if((f=fdopen(0,"r"))==NULL) { return false; } if((n=getline(&data,(size_t *)&n,f))<=0) { return false; } sep=QString(data).stripWhiteSpace(); // // Get message parts // while((n=getline(&data,(size_t *)&n,f))>0) { if(QString(data).stripWhiteSpace().contains(sep)>0) { // End of part if(fd>=0) { ftruncate(fd,lseek(fd,0,SEEK_CUR)-2); // Remove extraneous final CR/LF ::close(fd); fd=-1; } name=""; filename=""; headers.clear(); header=true; continue; } if(header) { // Read header if(QString(data).stripWhiteSpace().isEmpty()) { if(!headers["content-disposition"].isNull()) { QStringList fields; fields=headers["content-disposition"].split(";"); if(fields.size()>0) { if(fields[0].lower().stripWhiteSpace()=="form-data") { for(int i=1;i"+RDXmlEscape(value)+"\n"; } QString RDXmlField(const QString &tag,const char *value,const QString &attrs) { return RDXmlField(tag,QString(value),attrs); } QString RDXmlField(const QString &tag,const int value,const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } return QString("<")+tag+str+">"+QString().sprintf("%d",value)+"\n"; } QString RDXmlField(const QString &tag,const unsigned value,const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } return QString("<")+tag+str+">"+QString().sprintf("%u",value)+"\n"; } QString RDXmlField(const QString &tag,const bool value,const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } if(value) { return QString("<")+tag+str+">true\n"; } return QString("<")+tag+str+">false\n"; } QString RDXmlField(const QString &tag,const QDateTime &value, const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } if(value.isValid()) { return QString("<")+tag+str+">"+RDWriteXmlDateTime(value)+"\n"; } return RDXmlField(tag); } QString RDXmlField(const QString &tag,const QDate &value,const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } if(value.isValid()&&(!value.isNull())) { return QString("<")+tag+str+">"+RDWriteXmlDate(value)+"\n"; } return RDXmlField(tag); } QString RDXmlField(const QString &tag,const QTime &value,const QString &attrs) { QString str=""; if(!attrs.isEmpty()) { str=" "+attrs; } if(value.isValid()&&(!value.isNull())) { return QString("<")+tag+str+">"+RDWriteXmlTime(value)+"\n"; } return RDXmlField(tag); } QString RDXmlField(const QString &tag) { return QString("<")+tag+"/>\n"; } QString RDXmlEscape(const QString &str) { /* * Escape a string in accordance with XML-1.0 */ QString ret=str; ret.replace("&","&"); ret.replace("<","<"); ret.replace(">",">"); ret.replace("'","'"); ret.replace("\"","""); return ret; } QString RDXmlUnescape(const QString &str) { /* * Unescape a string in accordance with XML-1.0 */ QString ret=str; ret.replace("&","&"); ret.replace("<","<"); ret.replace(">",">"); ret.replace("'","'"); ret.replace(""","\""); return ret; } QString RDJsonPadding(int padding) { QString ret=""; for(int i=0;i","%3E"); ret.replace("#","%23"); ret.replace("\"","%22"); ret.replace("{","%7B"); ret.replace("}","%7D"); ret.replace("|","%7C"); ret.replace("\\","%5C"); ret.replace("^","%5E"); ret.replace("[","%5B"); ret.replace("]","%5D"); ret.replace("~","%7E"); return ret; } QString RDUrlUnescape(const QString &str) { /* * Unescape a string in accordance with RFC 2396 Section 2.4 */ QString ret=""; for(int i=0;i