1 /** 2 * Orthanc - A Lightweight, RESTful DICOM Store 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics 4 * Department, University Hospital of Liege, Belgium 5 * Copyright (C) 2017-2020 Osimis S.A., Belgium 6 * 7 * This program is free software: you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public License 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program. If not, see 19 * <http://www.gnu.org/licenses/>. 20 **/ 21 22 23 #include "../PrecompiledHeaders.h" 24 #include "LuaFunctionCall.h" 25 26 #include "../OrthancException.h" 27 #include "../Logging.h" 28 29 #if ORTHANC_ENABLE_DCMTK == 1 30 # include "../DicomParsing/FromDcmtkBridge.h" 31 #endif 32 33 #include <cassert> 34 #include <stdio.h> 35 #include <boost/lexical_cast.hpp> 36 37 namespace Orthanc 38 { CheckAlreadyExecuted()39 void LuaFunctionCall::CheckAlreadyExecuted() 40 { 41 if (isExecuted_) 42 { 43 throw OrthancException(ErrorCode_LuaAlreadyExecuted); 44 } 45 } 46 LuaFunctionCall(LuaContext & context,const char * functionName)47 LuaFunctionCall::LuaFunctionCall(LuaContext& context, 48 const char* functionName) : 49 context_(context), 50 isExecuted_(false) 51 { 52 // Clear the stack to fulfill the invariant 53 lua_settop(context_.lua_, 0); 54 lua_getglobal(context_.lua_, functionName); 55 } 56 PushString(const std::string & value)57 void LuaFunctionCall::PushString(const std::string& value) 58 { 59 CheckAlreadyExecuted(); 60 lua_pushlstring(context_.lua_, value.c_str(), value.size()); 61 } 62 PushBoolean(bool value)63 void LuaFunctionCall::PushBoolean(bool value) 64 { 65 CheckAlreadyExecuted(); 66 lua_pushboolean(context_.lua_, value); 67 } 68 PushInteger(int value)69 void LuaFunctionCall::PushInteger(int value) 70 { 71 CheckAlreadyExecuted(); 72 lua_pushinteger(context_.lua_, value); 73 } 74 PushDouble(double value)75 void LuaFunctionCall::PushDouble(double value) 76 { 77 CheckAlreadyExecuted(); 78 lua_pushnumber(context_.lua_, value); 79 } 80 PushJson(const Json::Value & value)81 void LuaFunctionCall::PushJson(const Json::Value& value) 82 { 83 CheckAlreadyExecuted(); 84 context_.PushJson(value); 85 } 86 ExecuteInternal(int numOutputs)87 void LuaFunctionCall::ExecuteInternal(int numOutputs) 88 { 89 CheckAlreadyExecuted(); 90 91 assert(lua_gettop(context_.lua_) >= 1); 92 int nargs = lua_gettop(context_.lua_) - 1; 93 int error = lua_pcall(context_.lua_, nargs, numOutputs, 0); 94 95 if (error) 96 { 97 assert(lua_gettop(context_.lua_) >= 1); 98 99 std::string description(lua_tostring(context_.lua_, -1)); 100 lua_pop(context_.lua_, 1); /* pop error message from the stack */ 101 102 throw OrthancException(ErrorCode_CannotExecuteLua, description); 103 } 104 105 if (lua_gettop(context_.lua_) < numOutputs) 106 { 107 throw OrthancException(ErrorCode_LuaBadOutput); 108 } 109 110 isExecuted_ = true; 111 } 112 ExecutePredicate()113 bool LuaFunctionCall::ExecutePredicate() 114 { 115 ExecuteInternal(1); 116 117 if (!lua_isboolean(context_.lua_, 1)) 118 { 119 throw OrthancException(ErrorCode_NotLuaPredicate); 120 } 121 122 return lua_toboolean(context_.lua_, 1) != 0; 123 } 124 125 ExecuteToJson(Json::Value & result,bool keepStrings)126 void LuaFunctionCall::ExecuteToJson(Json::Value& result, 127 bool keepStrings) 128 { 129 ExecuteInternal(1); 130 context_.GetJson(result, context_.lua_, lua_gettop(context_.lua_), keepStrings); 131 } 132 133 ExecuteToString(std::string & result)134 void LuaFunctionCall::ExecuteToString(std::string& result) 135 { 136 ExecuteInternal(1); 137 138 int top = lua_gettop(context_.lua_); 139 if (lua_isstring(context_.lua_, top)) 140 { 141 result = lua_tostring(context_.lua_, top); 142 } 143 else 144 { 145 throw OrthancException(ErrorCode_LuaReturnsNoString); 146 } 147 } 148 149 PushStringMap(const std::map<std::string,std::string> & value)150 void LuaFunctionCall::PushStringMap(const std::map<std::string, std::string>& value) 151 { 152 Json::Value json = Json::objectValue; 153 154 for (std::map<std::string, std::string>::const_iterator 155 it = value.begin(); it != value.end(); ++it) 156 { 157 json[it->first] = it->second; 158 } 159 160 PushJson(json); 161 } 162 163 PushDicom(const DicomMap & dicom)164 void LuaFunctionCall::PushDicom(const DicomMap& dicom) 165 { 166 DicomArray a(dicom); 167 PushDicom(a); 168 } 169 170 PushDicom(const DicomArray & dicom)171 void LuaFunctionCall::PushDicom(const DicomArray& dicom) 172 { 173 Json::Value value = Json::objectValue; 174 175 for (size_t i = 0; i < dicom.GetSize(); i++) 176 { 177 const DicomValue& v = dicom.GetElement(i).GetValue(); 178 std::string s = (v.IsNull() || v.IsBinary()) ? "" : v.GetContent(); 179 value[dicom.GetElement(i).GetTag().Format()] = s; 180 } 181 182 PushJson(value); 183 } 184 Execute()185 void LuaFunctionCall::Execute() 186 { 187 ExecuteInternal(0); 188 } 189 190 191 #if ORTHANC_ENABLE_DCMTK == 1 ExecuteToDicom(DicomMap & target)192 void LuaFunctionCall::ExecuteToDicom(DicomMap& target) 193 { 194 Json::Value output; 195 ExecuteToJson(output, true /* keep strings */); 196 197 target.Clear(); 198 199 if (output.type() == Json::arrayValue && 200 output.size() == 0) 201 { 202 // This case happens for empty tables 203 return; 204 } 205 206 if (output.type() != Json::objectValue) 207 { 208 throw OrthancException(ErrorCode_LuaBadOutput, 209 "Lua: The script must return a table"); 210 } 211 212 Json::Value::Members members = output.getMemberNames(); 213 214 for (size_t i = 0; i < members.size(); i++) 215 { 216 if (output[members[i]].type() != Json::stringValue) 217 { 218 throw OrthancException(ErrorCode_LuaBadOutput, 219 "Lua: The script must return a table " 220 "mapping names of DICOM tags to strings"); 221 } 222 223 DicomTag tag(FromDcmtkBridge::ParseTag(members[i])); 224 target.SetValue(tag, output[members[i]].asString(), false); 225 } 226 } 227 #endif 228 } 229