// =============================== // PC-BSD REST/JSON API Server // Available under the 3-clause BSD License // Written by: Ken Moore July 2015 // ================================= #include "RestStructs.h" // === INPUT STRUCTURE === RestInputStruct::RestInputStruct(QString message, bool isRest){ HTTPVERSION = CurHttpVersion; //default value fullaccess = false; if(message.isEmpty()){ return; } //Pull out any REST headers //qDebug() << "Raw Message:" << message; if(!message.startsWith("{")){ //TO-DO if(isRest){ Header = message.section("{",0,0).split("\n"); Body = "{"+message.section("{",1, -1); }else{ //Encrypted message body (via sysadm-bridge?) bridgeID = message.section("\n",0,0); Header << bridgeID; Body = message.section("\n",1,-1); } }else{ Body = message; } if(!Header.isEmpty() && isRest){ QString line = Header.takeFirst(); //The first line is special (not a generic header) VERB = line.section(" ",0,0); URI = line.section(" ",1,1); HTTPVERSION = line.section(" ",2,2); //Body = message.remove(Header.join("\n")+"\n"); //chop the headers off the front if(!Header.filter("Authorization:").isEmpty()){ line = Header.filter("Authorization:").takeFirst().section("Authorization: ",1,50).simplified(); if(line.section(" ",0,0).toLower()=="basic"){ //Convert the base64-encoded string to the plain "user:pass" string QByteArray ba; ba.append(line.section(" ",1,1)); auth = QByteArray::fromBase64(ba); } } } //Now Parse out the Body into the JSON fields and/or arguments structure //NOTE: if the body of the message is encrypted, then it needs to be decrypted outside the struct first, // then run the "ParseBodyIntoJson()" function to read/convert the data as needed. //qDebug() << "Got request:" << message << isRest << Header << bridgeID; if(Header.isEmpty() || isRest){ //no other data processing needed ParseBodyIntoJson(); } } RestInputStruct::~RestInputStruct(){} void RestInputStruct::ParseBodyIntoJson(){ //qDebug() << "Parse Body Into JSON"; while(Body.endsWith("\n")){ Body.chop(1); } if(Body.startsWith("{") && Body.endsWith("}") ){ QJsonDocument doc = QJsonDocument::fromJson(Body.toUtf8()); if(!doc.isNull() && doc.isObject() ){ //Valid JSON found if(doc.object().contains("namespace") ){ namesp = doc.object().value("namespace").toString(); } if(doc.object().contains("name") ){ name = doc.object().value("name").toString(); } if(doc.object().contains("id") ){ id = doc.object().value("id").toString(); } if(doc.object().contains("args") ){ args = doc.object().value("args"); } else{ //no args structure - treat the entire body as the arguments struct args = doc.object(); } } }else{ qDebug() << " -Could not find JSON!!"; qDebug() << " - Body:" << Body; } //Now do any REST -> JSON conversions if necessary if(!URI.isEmpty()){ //TO-DO name = URI.section("/",-1); //last entry namesp = URI.section("/",1,1); //URI excluding name } } // === OUTPUT STRUCTURE === QString RestOutputStruct::assembleMessage(){ if( !in_struct.VERB.isEmpty() ){ //REST output syntax QStringList headers; QString firstline = in_struct.HTTPVERSION.simplified(); if(firstline.isEmpty()){ firstline = CurHttpVersion.simplified(); }//default value QString Body; if(!out_args.isNull()){ QJsonObject obj; obj.insert("args", out_args); Body = QJsonDocument(obj).toJson(); } switch(CODE){ case PROCESSING: firstline.append(" 102 Processing"); break; case OK: firstline.append(" 200 OK"); break; case CREATED: firstline.append(" 201 Created"); break; case ACCEPTED: firstline.append(" 202 Accepted"); break; case NOCONTENT: firstline.append(" 204 No Content"); break; case RESETCONTENT: firstline.append(" 205 Reset Content"); break; case PARTIALCONTENT: firstline.append(" 206 Partial Content"); break; case BADREQUEST: firstline.append(" 400 Bad Request"); break; case UNAUTHORIZED: firstline.append(" 401 Unauthorized"); break; case FORBIDDEN: firstline.append(" 403 Forbidden"); break; case NOTFOUND: firstline.append(" 404 Not Found"); break; } headers << firstline; headers << "Server: SysAdm/1.0"; headers << "Date: "+QDateTime::currentDateTime().toString(Qt::ISODate).simplified(); if(!Header.isEmpty()) { for (int i = 0; i < Header.size(); ++i) headers << Header.at(i).simplified(); } //Now add the body of the return if(!Body.isEmpty()){ headers << "Content-Length: "+QString::number(Body.toUtf8().size()); } //number of bytes for the body headers << ""; headers << Body; return headers.join("\r\n"); }else{ //JSON output (load all the input fields for the moment) QString oname = "response"; //use this for valid responses (input ID found) if(in_struct.id.isEmpty()){ oname = in_struct.name; } QString onamesp = in_struct.namesp; QString oid = in_struct.id; if(CODE!=OK){ //Format the output based on the type of error QJsonObject out_err; switch(CODE){ case PROCESSING: //oname = onamesp = "error"; out_err.insert("code","102"); out_err.insert("message", "Processing"); break; case CREATED: //oname = onamesp = "error"; out_err.insert("code","201"); out_err.insert("message", "Created"); break; case ACCEPTED: //oname = onamesp = "error"; out_err.insert("code","202"); out_err.insert("message", "Accepted"); break; case NOCONTENT: oname = onamesp = "error"; out_err.insert("code","204"); out_err.insert("message", "No Content"); break; case RESETCONTENT: //oname = onamesp = "error"; out_err.insert("code","205"); out_err.insert("message", "Reset Content"); break; case PARTIALCONTENT: oname = onamesp = "error"; out_err.insert("code","206"); out_err.insert("message", "Partial Content"); break; case BADREQUEST: oname = onamesp = "error"; out_err.insert("code","400"); out_err.insert("message", "Bad Request"); break; case UNAUTHORIZED: oname = onamesp = "error"; out_err.insert("code","401"); out_err.insert("message", "Unauthorized"); break; case FORBIDDEN: oname = onamesp = "error"; out_err.insert("code","403"); out_err.insert("message", "Forbidden"); break; case NOTFOUND: oname = onamesp = "error"; out_err.insert("code","404"); out_err.insert("message", "Not Found"); break; default: break; } out_args = out_err; } //Now assemple the JSON output QJsonObject obj; obj.insert("namespace",onamesp); obj.insert("name",oname); obj.insert("id",oid); obj.insert("args", out_args); //Convert the JSON to string form QJsonDocument doc(obj); return doc.toJson(QJsonDocument::Compact); } }