cmCTestSubmitHandler.cxx 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCTestSubmitHandler.h"
  4. #include <cm_curl.h>
  5. #include <cm_jsoncpp_reader.h>
  6. #include <cm_jsoncpp_value.h>
  7. #include <cmsys/Process.h>
  8. #include <sstream>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include "cmCTest.h"
  12. #include "cmCTestCurl.h"
  13. #include "cmCTestScriptHandler.h"
  14. #include "cmCurl.h"
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmProcessOutput.h"
  17. #include "cmState.h"
  18. #include "cmSystemTools.h"
  19. #include "cmThirdParty.h"
  20. #include "cmXMLParser.h"
  21. #include "cmake.h"
  22. #if defined(CTEST_USE_XMLRPC)
  23. #include "cmVersion.h"
  24. #include <cm_xmlrpc.h>
  25. #include <sys/stat.h>
  26. #endif
  27. #define SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT 120
  28. typedef std::vector<char> cmCTestSubmitHandlerVectorOfChar;
  29. class cmCTestSubmitHandler::ResponseParser : public cmXMLParser
  30. {
  31. public:
  32. ResponseParser() { this->Status = STATUS_OK; }
  33. ~ResponseParser() CM_OVERRIDE {}
  34. public:
  35. enum StatusType
  36. {
  37. STATUS_OK,
  38. STATUS_WARNING,
  39. STATUS_ERROR
  40. };
  41. StatusType Status;
  42. std::string Filename;
  43. std::string MD5;
  44. std::string Message;
  45. private:
  46. std::vector<char> CurrentValue;
  47. std::string GetCurrentValue()
  48. {
  49. std::string val;
  50. if (!this->CurrentValue.empty()) {
  51. val.assign(&this->CurrentValue[0], this->CurrentValue.size());
  52. }
  53. return val;
  54. }
  55. void StartElement(const std::string& /*name*/,
  56. const char** /*atts*/) CM_OVERRIDE
  57. {
  58. this->CurrentValue.clear();
  59. }
  60. void CharacterDataHandler(const char* data, int length) CM_OVERRIDE
  61. {
  62. this->CurrentValue.insert(this->CurrentValue.end(), data, data + length);
  63. }
  64. void EndElement(const std::string& name) CM_OVERRIDE
  65. {
  66. if (name == "status") {
  67. std::string status = cmSystemTools::UpperCase(this->GetCurrentValue());
  68. if (status == "OK" || status == "SUCCESS") {
  69. this->Status = STATUS_OK;
  70. } else if (status == "WARNING") {
  71. this->Status = STATUS_WARNING;
  72. } else {
  73. this->Status = STATUS_ERROR;
  74. }
  75. } else if (name == "filename") {
  76. this->Filename = this->GetCurrentValue();
  77. } else if (name == "md5") {
  78. this->MD5 = this->GetCurrentValue();
  79. } else if (name == "message") {
  80. this->Message = this->GetCurrentValue();
  81. }
  82. }
  83. };
  84. static size_t cmCTestSubmitHandlerWriteMemoryCallback(void* ptr, size_t size,
  85. size_t nmemb, void* data)
  86. {
  87. int realsize = (int)(size * nmemb);
  88. cmCTestSubmitHandlerVectorOfChar* vec =
  89. static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  90. const char* chPtr = static_cast<char*>(ptr);
  91. vec->insert(vec->end(), chPtr, chPtr + realsize);
  92. return realsize;
  93. }
  94. static size_t cmCTestSubmitHandlerCurlDebugCallback(CURL* /*unused*/,
  95. curl_infotype /*unused*/,
  96. char* chPtr, size_t size,
  97. void* data)
  98. {
  99. cmCTestSubmitHandlerVectorOfChar* vec =
  100. static_cast<cmCTestSubmitHandlerVectorOfChar*>(data);
  101. vec->insert(vec->end(), chPtr, chPtr + size);
  102. return size;
  103. }
  104. cmCTestSubmitHandler::cmCTestSubmitHandler()
  105. : HTTPProxy()
  106. , FTPProxy()
  107. {
  108. this->Initialize();
  109. }
  110. void cmCTestSubmitHandler::Initialize()
  111. {
  112. // We submit all available parts by default.
  113. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  114. p = cmCTest::Part(p + 1)) {
  115. this->SubmitPart[p] = true;
  116. }
  117. this->CDash = false;
  118. this->HasWarnings = false;
  119. this->HasErrors = false;
  120. this->Superclass::Initialize();
  121. this->HTTPProxy = "";
  122. this->HTTPProxyType = 0;
  123. this->HTTPProxyAuth = "";
  124. this->FTPProxy = "";
  125. this->FTPProxyType = 0;
  126. this->LogFile = CM_NULLPTR;
  127. this->Files.clear();
  128. }
  129. bool cmCTestSubmitHandler::SubmitUsingFTP(const std::string& localprefix,
  130. const std::set<std::string>& files,
  131. const std::string& remoteprefix,
  132. const std::string& url)
  133. {
  134. CURL* curl;
  135. CURLcode res;
  136. FILE* ftpfile;
  137. char error_buffer[1024];
  138. /* In windows, this will init the winsock stuff */
  139. ::curl_global_init(CURL_GLOBAL_ALL);
  140. cmCTest::SetOfStrings::const_iterator file;
  141. for (file = files.begin(); file != files.end(); ++file) {
  142. /* get a curl handle */
  143. curl = curl_easy_init();
  144. if (curl) {
  145. // Using proxy
  146. if (this->FTPProxyType > 0) {
  147. curl_easy_setopt(curl, CURLOPT_PROXY, this->FTPProxy.c_str());
  148. switch (this->FTPProxyType) {
  149. case 2:
  150. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  151. break;
  152. case 3:
  153. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  154. break;
  155. default:
  156. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  157. }
  158. }
  159. // enable uploading
  160. ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  161. // if there is little to no activity for too long stop submitting
  162. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
  163. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
  164. SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  165. ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  166. std::string local_file = *file;
  167. if (!cmSystemTools::FileExists(local_file.c_str())) {
  168. local_file = localprefix + "/" + *file;
  169. }
  170. std::string upload_as =
  171. url + "/" + remoteprefix + cmSystemTools::GetFilenameName(*file);
  172. if (!cmSystemTools::FileExists(local_file.c_str())) {
  173. cmCTestLog(this->CTest, ERROR_MESSAGE,
  174. " Cannot find file: " << local_file << std::endl);
  175. ::curl_easy_cleanup(curl);
  176. ::curl_global_cleanup();
  177. return false;
  178. }
  179. unsigned long filelen = cmSystemTools::FileLength(local_file);
  180. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  181. *this->LogFile << "\tUpload file: " << local_file << " to " << upload_as
  182. << std::endl;
  183. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  184. " Upload file: " << local_file << " to "
  185. << upload_as << std::endl,
  186. this->Quiet);
  187. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  188. // specify target
  189. ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str());
  190. // now specify which file to upload
  191. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  192. // and give the size of the upload (optional)
  193. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(filelen));
  194. // and give curl the buffer for errors
  195. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  196. // specify handler for output
  197. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  198. cmCTestSubmitHandlerWriteMemoryCallback);
  199. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  200. cmCTestSubmitHandlerCurlDebugCallback);
  201. /* we pass our 'chunk' struct to the callback function */
  202. cmCTestSubmitHandlerVectorOfChar chunk;
  203. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  204. ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk);
  205. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug);
  206. // Now run off and do what you've been told!
  207. res = ::curl_easy_perform(curl);
  208. if (!chunk.empty()) {
  209. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  210. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  211. << "]" << std::endl,
  212. this->Quiet);
  213. }
  214. if (!chunkDebug.empty()) {
  215. cmCTestOptionalLog(
  216. this->CTest, DEBUG, "CURL debug output: ["
  217. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  218. << std::endl,
  219. this->Quiet);
  220. }
  221. fclose(ftpfile);
  222. if (res) {
  223. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: "
  224. << local_file << std::endl);
  225. cmCTestLog(this->CTest, ERROR_MESSAGE,
  226. " Error message was: " << error_buffer << std::endl);
  227. *this->LogFile << " Error when uploading file: " << local_file
  228. << std::endl
  229. << " Error message was: " << error_buffer << std::endl
  230. << " Curl output was: ";
  231. // avoid dereference of empty vector
  232. if (!chunk.empty()) {
  233. *this->LogFile << cmCTestLogWrite(&*chunk.begin(), chunk.size());
  234. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  235. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  236. << std::endl);
  237. }
  238. *this->LogFile << std::endl;
  239. ::curl_easy_cleanup(curl);
  240. ::curl_global_cleanup();
  241. return false;
  242. }
  243. // always cleanup
  244. ::curl_easy_cleanup(curl);
  245. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  246. " Uploaded: " + local_file << std::endl,
  247. this->Quiet);
  248. }
  249. }
  250. ::curl_global_cleanup();
  251. return true;
  252. }
  253. // Uploading files is simpler
  254. bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix,
  255. const std::set<std::string>& files,
  256. const std::string& remoteprefix,
  257. const std::string& url)
  258. {
  259. CURL* curl;
  260. CURLcode res;
  261. FILE* ftpfile;
  262. char error_buffer[1024];
  263. struct curl_slist* headers =
  264. ::curl_slist_append(CM_NULLPTR, "Content-Type: text/xml");
  265. /* In windows, this will init the winsock stuff */
  266. ::curl_global_init(CURL_GLOBAL_ALL);
  267. std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod"));
  268. std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
  269. std::vector<std::string> args;
  270. cmSystemTools::ExpandListArgument(curlopt, args);
  271. bool verifyPeerOff = false;
  272. bool verifyHostOff = false;
  273. for (std::vector<std::string>::iterator i = args.begin(); i != args.end();
  274. ++i) {
  275. if (*i == "CURLOPT_SSL_VERIFYPEER_OFF") {
  276. verifyPeerOff = true;
  277. }
  278. if (*i == "CURLOPT_SSL_VERIFYHOST_OFF") {
  279. verifyHostOff = true;
  280. }
  281. }
  282. std::string::size_type kk;
  283. cmCTest::SetOfStrings::const_iterator file;
  284. for (file = files.begin(); file != files.end(); ++file) {
  285. /* get a curl handle */
  286. curl = curl_easy_init();
  287. if (curl) {
  288. cmCurlSetCAInfo(curl);
  289. if (verifyPeerOff) {
  290. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  291. " Set CURLOPT_SSL_VERIFYPEER to off\n",
  292. this->Quiet);
  293. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  294. }
  295. if (verifyHostOff) {
  296. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  297. " Set CURLOPT_SSL_VERIFYHOST to off\n",
  298. this->Quiet);
  299. curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
  300. }
  301. // Using proxy
  302. if (this->HTTPProxyType > 0) {
  303. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  304. switch (this->HTTPProxyType) {
  305. case 2:
  306. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  307. break;
  308. case 3:
  309. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  310. break;
  311. default:
  312. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  313. if (!this->HTTPProxyAuth.empty()) {
  314. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  315. this->HTTPProxyAuth.c_str());
  316. }
  317. }
  318. }
  319. if (this->CTest->ShouldUseHTTP10()) {
  320. curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
  321. }
  322. // enable HTTP ERROR parsing
  323. curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
  324. /* enable uploading */
  325. curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);
  326. // if there is little to no activity for too long stop submitting
  327. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1);
  328. ::curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME,
  329. SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  330. /* HTTP PUT please */
  331. ::curl_easy_setopt(curl, CURLOPT_PUT, 1);
  332. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  333. // Be sure to set Content-Type to satisfy fussy modsecurity rules
  334. ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  335. std::string local_file = *file;
  336. if (!cmSystemTools::FileExists(local_file.c_str())) {
  337. local_file = localprefix + "/" + *file;
  338. }
  339. std::string remote_file =
  340. remoteprefix + cmSystemTools::GetFilenameName(*file);
  341. *this->LogFile << "\tUpload file: " << local_file << " to "
  342. << remote_file << std::endl;
  343. std::string ofile = "";
  344. for (kk = 0; kk < remote_file.size(); kk++) {
  345. char c = remote_file[kk];
  346. char hexCh[4] = { 0, 0, 0, 0 };
  347. hexCh[0] = c;
  348. switch (c) {
  349. case '+':
  350. case '?':
  351. case '/':
  352. case '\\':
  353. case '&':
  354. case ' ':
  355. case '=':
  356. case '%':
  357. sprintf(hexCh, "%%%02X", (int)c);
  358. ofile.append(hexCh);
  359. break;
  360. default:
  361. ofile.append(hexCh);
  362. }
  363. }
  364. std::string upload_as = url +
  365. ((url.find('?') == std::string::npos) ? '?' : '&') + "FileName=" +
  366. ofile;
  367. upload_as += "&MD5=";
  368. if (cmSystemTools::IsOn(this->GetOption("InternalTest"))) {
  369. upload_as += "bad_md5sum";
  370. } else {
  371. char md5[33];
  372. cmSystemTools::ComputeFileMD5(local_file, md5);
  373. md5[32] = 0;
  374. upload_as += md5;
  375. }
  376. if (!cmSystemTools::FileExists(local_file.c_str())) {
  377. cmCTestLog(this->CTest, ERROR_MESSAGE,
  378. " Cannot find file: " << local_file << std::endl);
  379. ::curl_easy_cleanup(curl);
  380. ::curl_slist_free_all(headers);
  381. ::curl_global_cleanup();
  382. return false;
  383. }
  384. unsigned long filelen = cmSystemTools::FileLength(local_file);
  385. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  386. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  387. " Upload file: " << local_file << " to "
  388. << upload_as << " Size: "
  389. << filelen << std::endl,
  390. this->Quiet);
  391. // specify target
  392. ::curl_easy_setopt(curl, CURLOPT_URL, upload_as.c_str());
  393. // now specify which file to upload
  394. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  395. // and give the size of the upload (optional)
  396. ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(filelen));
  397. // and give curl the buffer for errors
  398. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  399. // specify handler for output
  400. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  401. cmCTestSubmitHandlerWriteMemoryCallback);
  402. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  403. cmCTestSubmitHandlerCurlDebugCallback);
  404. /* we pass our 'chunk' struct to the callback function */
  405. cmCTestSubmitHandlerVectorOfChar chunk;
  406. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  407. ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk);
  408. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug);
  409. // Now run off and do what you've been told!
  410. res = ::curl_easy_perform(curl);
  411. if (!chunk.empty()) {
  412. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  413. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  414. << "]" << std::endl,
  415. this->Quiet);
  416. this->ParseResponse(chunk);
  417. }
  418. if (!chunkDebug.empty()) {
  419. cmCTestOptionalLog(
  420. this->CTest, DEBUG, "CURL debug output: ["
  421. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  422. << std::endl,
  423. this->Quiet);
  424. }
  425. // If curl failed for any reason, or checksum fails, wait and retry
  426. //
  427. if (res != CURLE_OK || this->HasErrors) {
  428. std::string retryDelay = this->GetOption("RetryDelay") == CM_NULLPTR
  429. ? ""
  430. : this->GetOption("RetryDelay");
  431. std::string retryCount = this->GetOption("RetryCount") == CM_NULLPTR
  432. ? ""
  433. : this->GetOption("RetryCount");
  434. int delay = retryDelay == ""
  435. ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryDelay")
  436. .c_str())
  437. : atoi(retryDelay.c_str());
  438. int count = retryCount == ""
  439. ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryCount")
  440. .c_str())
  441. : atoi(retryCount.c_str());
  442. for (int i = 0; i < count; i++) {
  443. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  444. " Submit failed, waiting " << delay
  445. << " seconds...\n",
  446. this->Quiet);
  447. double stop = cmSystemTools::GetTime() + delay;
  448. while (cmSystemTools::GetTime() < stop) {
  449. cmSystemTools::Delay(100);
  450. }
  451. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  452. " Retry submission: Attempt "
  453. << (i + 1) << " of " << count << std::endl,
  454. this->Quiet);
  455. ::fclose(ftpfile);
  456. ftpfile = cmsys::SystemTools::Fopen(local_file, "rb");
  457. ::curl_easy_setopt(curl, CURLOPT_INFILE, ftpfile);
  458. chunk.clear();
  459. chunkDebug.clear();
  460. this->HasErrors = false;
  461. res = ::curl_easy_perform(curl);
  462. if (!chunk.empty()) {
  463. cmCTestOptionalLog(
  464. this->CTest, DEBUG, "CURL output: ["
  465. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  466. << std::endl,
  467. this->Quiet);
  468. this->ParseResponse(chunk);
  469. }
  470. if (res == CURLE_OK && !this->HasErrors) {
  471. break;
  472. }
  473. }
  474. }
  475. fclose(ftpfile);
  476. if (res) {
  477. cmCTestLog(this->CTest, ERROR_MESSAGE, " Error when uploading file: "
  478. << local_file << std::endl);
  479. cmCTestLog(this->CTest, ERROR_MESSAGE,
  480. " Error message was: " << error_buffer << std::endl);
  481. *this->LogFile << " Error when uploading file: " << local_file
  482. << std::endl
  483. << " Error message was: " << error_buffer
  484. << std::endl;
  485. // avoid deref of begin for zero size array
  486. if (!chunk.empty()) {
  487. *this->LogFile << " Curl output was: "
  488. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  489. << std::endl;
  490. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  491. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  492. << std::endl);
  493. }
  494. ::curl_easy_cleanup(curl);
  495. ::curl_slist_free_all(headers);
  496. ::curl_global_cleanup();
  497. return false;
  498. }
  499. // always cleanup
  500. ::curl_easy_cleanup(curl);
  501. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  502. " Uploaded: " + local_file << std::endl,
  503. this->Quiet);
  504. }
  505. }
  506. ::curl_slist_free_all(headers);
  507. ::curl_global_cleanup();
  508. return true;
  509. }
  510. void cmCTestSubmitHandler::ParseResponse(
  511. cmCTestSubmitHandlerVectorOfChar chunk)
  512. {
  513. std::string output = "";
  514. output.append(chunk.begin(), chunk.end());
  515. if (output.find("<cdash") != output.npos) {
  516. ResponseParser parser;
  517. parser.Parse(output.c_str());
  518. if (parser.Status != ResponseParser::STATUS_OK) {
  519. this->HasErrors = true;
  520. cmCTestLog(this->CTest, HANDLER_OUTPUT,
  521. " Submission failed: " << parser.Message << std::endl);
  522. return;
  523. }
  524. }
  525. output = cmSystemTools::UpperCase(output);
  526. if (output.find("WARNING") != std::string::npos) {
  527. this->HasWarnings = true;
  528. }
  529. if (output.find("ERROR") != std::string::npos) {
  530. this->HasErrors = true;
  531. }
  532. if (this->HasWarnings || this->HasErrors) {
  533. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Server Response:\n"
  534. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "\n");
  535. }
  536. }
  537. bool cmCTestSubmitHandler::TriggerUsingHTTP(const std::set<std::string>& files,
  538. const std::string& remoteprefix,
  539. const std::string& url)
  540. {
  541. CURL* curl;
  542. char error_buffer[1024];
  543. /* In windows, this will init the winsock stuff */
  544. ::curl_global_init(CURL_GLOBAL_ALL);
  545. cmCTest::SetOfStrings::const_iterator file;
  546. for (file = files.begin(); file != files.end(); ++file) {
  547. /* get a curl handle */
  548. curl = curl_easy_init();
  549. if (curl) {
  550. // Using proxy
  551. if (this->HTTPProxyType > 0) {
  552. curl_easy_setopt(curl, CURLOPT_PROXY, this->HTTPProxy.c_str());
  553. switch (this->HTTPProxyType) {
  554. case 2:
  555. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
  556. break;
  557. case 3:
  558. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
  559. break;
  560. default:
  561. curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
  562. if (!this->HTTPProxyAuth.empty()) {
  563. curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD,
  564. this->HTTPProxyAuth.c_str());
  565. }
  566. }
  567. }
  568. ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
  569. // and give curl the buffer for errors
  570. ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer);
  571. // specify handler for output
  572. ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
  573. cmCTestSubmitHandlerWriteMemoryCallback);
  574. ::curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
  575. cmCTestSubmitHandlerCurlDebugCallback);
  576. /* we pass our 'chunk' struct to the callback function */
  577. cmCTestSubmitHandlerVectorOfChar chunk;
  578. cmCTestSubmitHandlerVectorOfChar chunkDebug;
  579. ::curl_easy_setopt(curl, CURLOPT_FILE, (void*)&chunk);
  580. ::curl_easy_setopt(curl, CURLOPT_DEBUGDATA, (void*)&chunkDebug);
  581. std::string rfile = remoteprefix + cmSystemTools::GetFilenameName(*file);
  582. std::string ofile = "";
  583. std::string::iterator kk;
  584. for (kk = rfile.begin(); kk < rfile.end(); ++kk) {
  585. char c = *kk;
  586. char hexCh[4] = { 0, 0, 0, 0 };
  587. hexCh[0] = c;
  588. switch (c) {
  589. case '+':
  590. case '?':
  591. case '/':
  592. case '\\':
  593. case '&':
  594. case ' ':
  595. case '=':
  596. case '%':
  597. sprintf(hexCh, "%%%02X", (int)c);
  598. ofile.append(hexCh);
  599. break;
  600. default:
  601. ofile.append(hexCh);
  602. }
  603. }
  604. std::string turl = url +
  605. ((url.find('?') == std::string::npos) ? '?' : '&') + "xmlfile=" +
  606. ofile;
  607. *this->LogFile << "Trigger url: " << turl << std::endl;
  608. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  609. " Trigger url: " << turl << std::endl, this->Quiet);
  610. curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  611. curl_easy_setopt(curl, CURLOPT_URL, turl.c_str());
  612. if (curl_easy_perform(curl)) {
  613. cmCTestLog(this->CTest, ERROR_MESSAGE,
  614. " Error when triggering: " << turl << std::endl);
  615. cmCTestLog(this->CTest, ERROR_MESSAGE,
  616. " Error message was: " << error_buffer << std::endl);
  617. *this->LogFile << "\tTriggering failed with error: " << error_buffer
  618. << std::endl
  619. << " Error message was: " << error_buffer
  620. << std::endl;
  621. if (!chunk.empty()) {
  622. *this->LogFile << " Curl output was: "
  623. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  624. << std::endl;
  625. cmCTestLog(this->CTest, ERROR_MESSAGE, "CURL output: ["
  626. << cmCTestLogWrite(&*chunk.begin(), chunk.size()) << "]"
  627. << std::endl);
  628. }
  629. ::curl_easy_cleanup(curl);
  630. ::curl_global_cleanup();
  631. return false;
  632. }
  633. if (!chunk.empty()) {
  634. cmCTestOptionalLog(this->CTest, DEBUG, "CURL output: ["
  635. << cmCTestLogWrite(&*chunk.begin(), chunk.size())
  636. << "]" << std::endl,
  637. this->Quiet);
  638. }
  639. if (!chunkDebug.empty()) {
  640. cmCTestOptionalLog(
  641. this->CTest, DEBUG, "CURL debug output: ["
  642. << cmCTestLogWrite(&*chunkDebug.begin(), chunkDebug.size()) << "]"
  643. << std::endl,
  644. this->Quiet);
  645. }
  646. // always cleanup
  647. ::curl_easy_cleanup(curl);
  648. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, std::endl,
  649. this->Quiet);
  650. }
  651. }
  652. ::curl_global_cleanup();
  653. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  654. " Dart server triggered..." << std::endl, this->Quiet);
  655. return true;
  656. }
  657. bool cmCTestSubmitHandler::SubmitUsingSCP(const std::string& scp_command,
  658. const std::string& localprefix,
  659. const std::set<std::string>& files,
  660. const std::string& remoteprefix,
  661. const std::string& url)
  662. {
  663. if (scp_command.empty() || localprefix.empty() || files.empty() ||
  664. remoteprefix.empty() || url.empty()) {
  665. return 0;
  666. }
  667. std::vector<const char*> argv;
  668. argv.push_back(scp_command.c_str()); // Scp command
  669. argv.push_back(scp_command.c_str()); // Dummy string for file
  670. argv.push_back(scp_command.c_str()); // Dummy string for remote url
  671. argv.push_back(CM_NULLPTR);
  672. cmsysProcess* cp = cmsysProcess_New();
  673. cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
  674. // cmsysProcess_SetTimeout(cp, timeout);
  675. int problems = 0;
  676. cmCTest::SetOfStrings::const_iterator file;
  677. for (file = files.begin(); file != files.end(); ++file) {
  678. int retVal;
  679. std::string lfname = localprefix;
  680. cmSystemTools::ConvertToUnixSlashes(lfname);
  681. lfname += "/" + *file;
  682. lfname = cmSystemTools::ConvertToOutputPath(lfname.c_str());
  683. argv[1] = lfname.c_str();
  684. std::string rfname = url + "/" + remoteprefix + *file;
  685. argv[2] = rfname.c_str();
  686. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Execute \""
  687. << argv[0] << "\" \"" << argv[1] << "\" \"" << argv[2]
  688. << "\"" << std::endl,
  689. this->Quiet);
  690. *this->LogFile << "Execute \"" << argv[0] << "\" \"" << argv[1] << "\" \""
  691. << argv[2] << "\"" << std::endl;
  692. cmsysProcess_SetCommand(cp, &*argv.begin());
  693. cmsysProcess_Execute(cp);
  694. char* data;
  695. int length;
  696. cmProcessOutput processOutput;
  697. std::string strdata;
  698. while (cmsysProcess_WaitForData(cp, &data, &length, CM_NULLPTR)) {
  699. processOutput.DecodeText(data, length, strdata);
  700. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  701. cmCTestLogWrite(strdata.c_str(), strdata.size()),
  702. this->Quiet);
  703. }
  704. processOutput.DecodeText(std::string(), strdata);
  705. if (!strdata.empty()) {
  706. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  707. cmCTestLogWrite(strdata.c_str(), strdata.size()),
  708. this->Quiet);
  709. }
  710. cmsysProcess_WaitForExit(cp, CM_NULLPTR);
  711. int result = cmsysProcess_GetState(cp);
  712. if (result == cmsysProcess_State_Exited) {
  713. retVal = cmsysProcess_GetExitValue(cp);
  714. if (retVal != 0) {
  715. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  716. "\tSCP returned: " << retVal << std::endl,
  717. this->Quiet);
  718. *this->LogFile << "\tSCP returned: " << retVal << std::endl;
  719. problems++;
  720. }
  721. } else if (result == cmsysProcess_State_Exception) {
  722. retVal = cmsysProcess_GetExitException(cp);
  723. cmCTestLog(this->CTest, ERROR_MESSAGE,
  724. "\tThere was an exception: " << retVal << std::endl);
  725. *this->LogFile << "\tThere was an exception: " << retVal << std::endl;
  726. problems++;
  727. } else if (result == cmsysProcess_State_Expired) {
  728. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tThere was a timeout"
  729. << std::endl);
  730. *this->LogFile << "\tThere was a timeout" << std::endl;
  731. problems++;
  732. } else if (result == cmsysProcess_State_Error) {
  733. cmCTestLog(this->CTest, ERROR_MESSAGE, "\tError executing SCP: "
  734. << cmsysProcess_GetErrorString(cp) << std::endl);
  735. *this->LogFile << "\tError executing SCP: "
  736. << cmsysProcess_GetErrorString(cp) << std::endl;
  737. problems++;
  738. }
  739. }
  740. cmsysProcess_Delete(cp);
  741. return problems == 0;
  742. }
  743. bool cmCTestSubmitHandler::SubmitUsingCP(const std::string& localprefix,
  744. const std::set<std::string>& files,
  745. const std::string& remoteprefix,
  746. const std::string& destination)
  747. {
  748. if (localprefix.empty() || files.empty() || remoteprefix.empty() ||
  749. destination.empty()) {
  750. /* clang-format off */
  751. cmCTestLog(this->CTest, ERROR_MESSAGE,
  752. "Missing arguments for submit via cp:\n"
  753. << "\tlocalprefix: " << localprefix << "\n"
  754. << "\tNumber of files: " << files.size() << "\n"
  755. << "\tremoteprefix: " << remoteprefix << "\n"
  756. << "\tdestination: " << destination << std::endl);
  757. /* clang-format on */
  758. return 0;
  759. }
  760. cmCTest::SetOfStrings::const_iterator file;
  761. for (file = files.begin(); file != files.end(); ++file) {
  762. std::string lfname = localprefix;
  763. cmSystemTools::ConvertToUnixSlashes(lfname);
  764. lfname += "/" + *file;
  765. std::string rfname = destination + "/" + remoteprefix + *file;
  766. cmSystemTools::CopyFileAlways(lfname, rfname);
  767. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, " Copy file: "
  768. << lfname << " to " << rfname << std::endl,
  769. this->Quiet);
  770. }
  771. std::string tagDoneFile = destination + "/" + remoteprefix + "DONE";
  772. cmSystemTools::Touch(tagDoneFile, true);
  773. return true;
  774. }
  775. #if defined(CTEST_USE_XMLRPC)
  776. bool cmCTestSubmitHandler::SubmitUsingXMLRPC(
  777. const std::string& localprefix, const std::set<std::string>& files,
  778. const std::string& remoteprefix, const std::string& url)
  779. {
  780. xmlrpc_env env;
  781. char ctestString[] = "CTest";
  782. std::string ctestVersionString = cmVersion::GetCMakeVersion();
  783. char* ctestVersion = const_cast<char*>(ctestVersionString.c_str());
  784. std::string realURL = url + "/" + remoteprefix + "/Command/";
  785. /* Start up our XML-RPC client library. */
  786. xmlrpc_client_init(XMLRPC_CLIENT_NO_FLAGS, ctestString, ctestVersion);
  787. /* Initialize our error-handling environment. */
  788. xmlrpc_env_init(&env);
  789. /* Call the famous server at UserLand. */
  790. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Submitting to: "
  791. << realURL << " (" << remoteprefix << ")" << std::endl,
  792. this->Quiet);
  793. cmCTest::SetOfStrings::const_iterator file;
  794. for (file = files.begin(); file != files.end(); ++file) {
  795. xmlrpc_value* result;
  796. std::string local_file = *file;
  797. if (!cmSystemTools::FileExists(local_file.c_str())) {
  798. local_file = localprefix + "/" + *file;
  799. }
  800. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  801. " Submit file: " << local_file << std::endl,
  802. this->Quiet);
  803. struct stat st;
  804. if (::stat(local_file.c_str(), &st)) {
  805. cmCTestLog(this->CTest, ERROR_MESSAGE,
  806. " Cannot find file: " << local_file << std::endl);
  807. return false;
  808. }
  809. // off_t can be bigger than size_t. fread takes size_t.
  810. // make sure the file is not too big.
  811. if (static_cast<off_t>(static_cast<size_t>(st.st_size)) !=
  812. static_cast<off_t>(st.st_size)) {
  813. cmCTestLog(this->CTest, ERROR_MESSAGE, " File too big: " << local_file
  814. << std::endl);
  815. return false;
  816. }
  817. size_t fileSize = static_cast<size_t>(st.st_size);
  818. FILE* fp = cmsys::SystemTools::Fopen(local_file, "rb");
  819. if (!fp) {
  820. cmCTestLog(this->CTest, ERROR_MESSAGE,
  821. " Cannot open file: " << local_file << std::endl);
  822. return false;
  823. }
  824. unsigned char* fileBuffer = new unsigned char[fileSize];
  825. if (fread(fileBuffer, 1, fileSize, fp) != fileSize) {
  826. delete[] fileBuffer;
  827. fclose(fp);
  828. cmCTestLog(this->CTest, ERROR_MESSAGE,
  829. " Cannot read file: " << local_file << std::endl);
  830. return false;
  831. }
  832. fclose(fp);
  833. char remoteCommand[] = "Submit.put";
  834. char* pRealURL = const_cast<char*>(realURL.c_str());
  835. result = xmlrpc_client_call(&env, pRealURL, remoteCommand, "(6)",
  836. fileBuffer, (xmlrpc_int32)fileSize);
  837. delete[] fileBuffer;
  838. if (env.fault_occurred) {
  839. cmCTestLog(this->CTest, ERROR_MESSAGE, " Submission problem: "
  840. << env.fault_string << " (" << env.fault_code << ")"
  841. << std::endl);
  842. xmlrpc_env_clean(&env);
  843. xmlrpc_client_cleanup();
  844. return false;
  845. }
  846. /* Dispose of our result value. */
  847. xmlrpc_DECREF(result);
  848. }
  849. /* Clean up our error-handling environment. */
  850. xmlrpc_env_clean(&env);
  851. /* Shutdown our XML-RPC client library. */
  852. xmlrpc_client_cleanup();
  853. return true;
  854. }
  855. #else
  856. bool cmCTestSubmitHandler::SubmitUsingXMLRPC(
  857. std::string const& /*unused*/, std::set<std::string> const& /*unused*/,
  858. std::string const& /*unused*/, std::string const& /*unused*/)
  859. {
  860. return false;
  861. }
  862. #endif
  863. void cmCTestSubmitHandler::ConstructCDashURL(std::string& dropMethod,
  864. std::string& url)
  865. {
  866. dropMethod = this->CTest->GetCTestConfiguration("DropMethod");
  867. url = dropMethod;
  868. url += "://";
  869. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  870. url += this->CTest->GetCTestConfiguration("DropSiteUser");
  871. cmCTestOptionalLog(
  872. this->CTest, HANDLER_OUTPUT,
  873. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(), this->Quiet);
  874. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  875. url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
  876. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******", this->Quiet);
  877. }
  878. url += "@";
  879. }
  880. url += this->CTest->GetCTestConfiguration("DropSite") +
  881. this->CTest->GetCTestConfiguration("DropLocation");
  882. }
  883. int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file,
  884. std::string const& typeString)
  885. {
  886. if (file.empty()) {
  887. cmCTestLog(this->CTest, ERROR_MESSAGE, "Upload file not specified\n");
  888. return -1;
  889. }
  890. if (!cmSystemTools::FileExists(file)) {
  891. cmCTestLog(this->CTest, ERROR_MESSAGE, "Upload file not found: '"
  892. << file << "'\n");
  893. return -1;
  894. }
  895. cmCTestCurl curl(this->CTest);
  896. std::string curlopt(this->CTest->GetCTestConfiguration("CurlOptions"));
  897. std::vector<std::string> args;
  898. cmSystemTools::ExpandListArgument(curlopt, args);
  899. curl.SetCurlOptions(args);
  900. curl.SetTimeOutSeconds(SUBMIT_TIMEOUT_IN_SECONDS_DEFAULT);
  901. std::string dropMethod;
  902. std::string url;
  903. this->ConstructCDashURL(dropMethod, url);
  904. std::string::size_type pos = url.find("submit.php?");
  905. url = url.substr(0, pos + 10);
  906. if (!(dropMethod == "http" || dropMethod == "https")) {
  907. cmCTestLog(this->CTest, ERROR_MESSAGE,
  908. "Only http and https are supported for CDASH_UPLOAD\n");
  909. return -1;
  910. }
  911. char md5sum[33];
  912. md5sum[32] = 0;
  913. cmSystemTools::ComputeFileMD5(file, md5sum);
  914. // 1. request the buildid and check to see if the file
  915. // has already been uploaded
  916. // TODO I added support for subproject. You would need to add
  917. // a "&subproject=subprojectname" to the first POST.
  918. cmCTestScriptHandler* ch =
  919. static_cast<cmCTestScriptHandler*>(this->CTest->GetHandler("script"));
  920. cmake* cm = ch->GetCMake();
  921. const char* subproject = cm->GetState()->GetGlobalProperty("SubProject");
  922. // TODO: Encode values for a URL instead of trusting caller.
  923. std::ostringstream str;
  924. str << "project="
  925. << curl.Escape(this->CTest->GetCTestConfiguration("ProjectName")) << "&";
  926. if (subproject) {
  927. str << "subproject=" << curl.Escape(subproject) << "&";
  928. }
  929. str << "stamp=" << curl.Escape(this->CTest->GetCurrentTag()) << "-"
  930. << curl.Escape(this->CTest->GetTestModelString()) << "&"
  931. << "model=" << curl.Escape(this->CTest->GetTestModelString()) << "&"
  932. << "build="
  933. << curl.Escape(this->CTest->GetCTestConfiguration("BuildName")) << "&"
  934. << "site=" << curl.Escape(this->CTest->GetCTestConfiguration("Site"))
  935. << "&"
  936. << "track=" << curl.Escape(this->CTest->GetTestModelString()) << "&"
  937. << "starttime=" << (int)cmSystemTools::GetTime() << "&"
  938. << "endtime=" << (int)cmSystemTools::GetTime() << "&"
  939. << "datafilesmd5[0]=" << md5sum << "&"
  940. << "type=" << curl.Escape(typeString);
  941. std::string fields = str.str();
  942. cmCTestOptionalLog(this->CTest, DEBUG,
  943. "fields: " << fields << "\nurl:" << url
  944. << "\nfile: " << file << "\n",
  945. this->Quiet);
  946. std::string response;
  947. if (!curl.HttpRequest(url, fields, response)) {
  948. cmCTestLog(this->CTest, ERROR_MESSAGE, "Error in HttpRequest\n"
  949. << response);
  950. return -1;
  951. }
  952. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  953. "Request upload response: [" << response << "]\n",
  954. this->Quiet);
  955. Json::Value json;
  956. Json::Reader reader;
  957. if (!reader.parse(response, json)) {
  958. cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string ["
  959. << response << "]\n"
  960. << reader.getFormattedErrorMessages() << "\n");
  961. return -1;
  962. }
  963. if (json["status"].asInt() != 0) {
  964. cmCTestLog(this->CTest, ERROR_MESSAGE,
  965. "Bad status returned from CDash: " << json["status"].asInt());
  966. return -1;
  967. }
  968. if (json["datafilesmd5"].isArray()) {
  969. int datares = json["datafilesmd5"][0].asInt();
  970. if (datares == 1) {
  971. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  972. "File already exists on CDash, skip upload " << file
  973. << "\n",
  974. this->Quiet);
  975. return 0;
  976. }
  977. } else {
  978. cmCTestLog(this->CTest, ERROR_MESSAGE,
  979. "bad datafilesmd5 value in response " << response << "\n");
  980. return -1;
  981. }
  982. std::string upload_as = cmSystemTools::GetFilenameName(file);
  983. std::ostringstream fstr;
  984. fstr << "type=" << curl.Escape(typeString) << "&"
  985. << "md5=" << md5sum << "&"
  986. << "filename=" << curl.Escape(upload_as) << "&"
  987. << "buildid=" << json["buildid"].asString();
  988. if (!curl.UploadFile(file, url, fstr.str(), response)) {
  989. cmCTestLog(this->CTest, ERROR_MESSAGE, "error uploading to CDash. "
  990. << file << " " << url << " " << fstr.str());
  991. return -1;
  992. }
  993. if (!reader.parse(response, json)) {
  994. cmCTestLog(this->CTest, ERROR_MESSAGE, "error parsing json string ["
  995. << response << "]\n"
  996. << reader.getFormattedErrorMessages() << "\n");
  997. return -1;
  998. }
  999. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1000. "Upload file response: [" << response << "]\n",
  1001. this->Quiet);
  1002. return 0;
  1003. }
  1004. int cmCTestSubmitHandler::ProcessHandler()
  1005. {
  1006. const char* cdashUploadFile = this->GetOption("CDashUploadFile");
  1007. const char* cdashUploadType = this->GetOption("CDashUploadType");
  1008. if (cdashUploadFile && cdashUploadType) {
  1009. return this->HandleCDashUploadFile(cdashUploadFile, cdashUploadType);
  1010. }
  1011. std::string iscdash = this->CTest->GetCTestConfiguration("IsCDash");
  1012. // cdash does not need to trigger so just return true
  1013. if (!iscdash.empty()) {
  1014. this->CDash = true;
  1015. }
  1016. const std::string& buildDirectory =
  1017. this->CTest->GetCTestConfiguration("BuildDirectory");
  1018. if (buildDirectory.empty()) {
  1019. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1020. "Cannot find BuildDirectory key in the DartConfiguration.tcl"
  1021. << std::endl);
  1022. return -1;
  1023. }
  1024. if (getenv("HTTP_PROXY")) {
  1025. this->HTTPProxyType = 1;
  1026. this->HTTPProxy = getenv("HTTP_PROXY");
  1027. if (getenv("HTTP_PROXY_PORT")) {
  1028. this->HTTPProxy += ":";
  1029. this->HTTPProxy += getenv("HTTP_PROXY_PORT");
  1030. }
  1031. if (getenv("HTTP_PROXY_TYPE")) {
  1032. std::string type = getenv("HTTP_PROXY_TYPE");
  1033. // HTTP/SOCKS4/SOCKS5
  1034. if (type == "HTTP") {
  1035. this->HTTPProxyType = 1;
  1036. } else if (type == "SOCKS4") {
  1037. this->HTTPProxyType = 2;
  1038. } else if (type == "SOCKS5") {
  1039. this->HTTPProxyType = 3;
  1040. }
  1041. }
  1042. if (getenv("HTTP_PROXY_USER")) {
  1043. this->HTTPProxyAuth = getenv("HTTP_PROXY_USER");
  1044. }
  1045. if (getenv("HTTP_PROXY_PASSWD")) {
  1046. this->HTTPProxyAuth += ":";
  1047. this->HTTPProxyAuth += getenv("HTTP_PROXY_PASSWD");
  1048. }
  1049. }
  1050. if (getenv("FTP_PROXY")) {
  1051. this->FTPProxyType = 1;
  1052. this->FTPProxy = getenv("FTP_PROXY");
  1053. if (getenv("FTP_PROXY_PORT")) {
  1054. this->FTPProxy += ":";
  1055. this->FTPProxy += getenv("FTP_PROXY_PORT");
  1056. }
  1057. if (getenv("FTP_PROXY_TYPE")) {
  1058. std::string type = getenv("FTP_PROXY_TYPE");
  1059. // HTTP/SOCKS4/SOCKS5
  1060. if (type == "HTTP") {
  1061. this->FTPProxyType = 1;
  1062. } else if (type == "SOCKS4") {
  1063. this->FTPProxyType = 2;
  1064. } else if (type == "SOCKS5") {
  1065. this->FTPProxyType = 3;
  1066. }
  1067. }
  1068. }
  1069. if (!this->HTTPProxy.empty()) {
  1070. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1071. " Use HTTP Proxy: " << this->HTTPProxy << std::endl,
  1072. this->Quiet);
  1073. }
  1074. if (!this->FTPProxy.empty()) {
  1075. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1076. " Use FTP Proxy: " << this->FTPProxy << std::endl,
  1077. this->Quiet);
  1078. }
  1079. cmGeneratedFileStream ofs;
  1080. this->StartLogFile("Submit", ofs);
  1081. cmCTest::SetOfStrings files;
  1082. std::string prefix = this->GetSubmitResultsPrefix();
  1083. if (!this->Files.empty()) {
  1084. // Submit the explicitly selected files:
  1085. //
  1086. files.insert(this->Files.begin(), this->Files.end());
  1087. }
  1088. // Add to the list of files to submit from any selected, existing parts:
  1089. //
  1090. // TODO:
  1091. // Check if test is enabled
  1092. this->CTest->AddIfExists(cmCTest::PartUpdate, "Update.xml");
  1093. this->CTest->AddIfExists(cmCTest::PartConfigure, "Configure.xml");
  1094. this->CTest->AddIfExists(cmCTest::PartBuild, "Build.xml");
  1095. this->CTest->AddIfExists(cmCTest::PartTest, "Test.xml");
  1096. if (this->CTest->AddIfExists(cmCTest::PartCoverage, "Coverage.xml")) {
  1097. std::vector<std::string> gfiles;
  1098. std::string gpath =
  1099. buildDirectory + "/Testing/" + this->CTest->GetCurrentTag();
  1100. std::string::size_type glen = gpath.size() + 1;
  1101. gpath = gpath + "/CoverageLog*";
  1102. cmCTestOptionalLog(this->CTest, DEBUG,
  1103. "Globbing for: " << gpath << std::endl, this->Quiet);
  1104. if (cmSystemTools::SimpleGlob(gpath, gfiles, 1)) {
  1105. size_t cc;
  1106. for (cc = 0; cc < gfiles.size(); cc++) {
  1107. gfiles[cc] = gfiles[cc].substr(glen);
  1108. cmCTestOptionalLog(this->CTest, DEBUG,
  1109. "Glob file: " << gfiles[cc] << std::endl,
  1110. this->Quiet);
  1111. this->CTest->AddSubmitFile(cmCTest::PartCoverage, gfiles[cc].c_str());
  1112. }
  1113. } else {
  1114. cmCTestLog(this->CTest, ERROR_MESSAGE, "Problem globbing" << std::endl);
  1115. }
  1116. }
  1117. this->CTest->AddIfExists(cmCTest::PartMemCheck, "DynamicAnalysis.xml");
  1118. this->CTest->AddIfExists(cmCTest::PartMemCheck, "Purify.xml");
  1119. this->CTest->AddIfExists(cmCTest::PartNotes, "Notes.xml");
  1120. this->CTest->AddIfExists(cmCTest::PartUpload, "Upload.xml");
  1121. // Query parts for files to submit.
  1122. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  1123. p = cmCTest::Part(p + 1)) {
  1124. // Skip parts we are not submitting.
  1125. if (!this->SubmitPart[p]) {
  1126. continue;
  1127. }
  1128. // Submit files from this part.
  1129. std::vector<std::string> const& pfiles = this->CTest->GetSubmitFiles(p);
  1130. files.insert(pfiles.begin(), pfiles.end());
  1131. }
  1132. if (ofs) {
  1133. ofs << "Upload files:" << std::endl;
  1134. int cnt = 0;
  1135. cmCTest::SetOfStrings::iterator it;
  1136. for (it = files.begin(); it != files.end(); ++it) {
  1137. ofs << cnt << "\t" << *it << std::endl;
  1138. cnt++;
  1139. }
  1140. }
  1141. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "Submit files (using "
  1142. << this->CTest->GetCTestConfiguration("DropMethod")
  1143. << ")" << std::endl,
  1144. this->Quiet);
  1145. const char* specificTrack = this->CTest->GetSpecificTrack();
  1146. if (specificTrack) {
  1147. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1148. " Send to track: " << specificTrack << std::endl,
  1149. this->Quiet);
  1150. }
  1151. this->SetLogFile(&ofs);
  1152. std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod"));
  1153. if (dropMethod == "" || dropMethod == "ftp") {
  1154. ofs << "Using drop method: FTP" << std::endl;
  1155. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1156. " Using FTP submit method" << std::endl
  1157. << " Drop site: ftp://",
  1158. this->Quiet);
  1159. std::string url = "ftp://";
  1160. url += cmCTest::MakeURLSafe(
  1161. this->CTest->GetCTestConfiguration("DropSiteUser")) +
  1162. ":" + cmCTest::MakeURLSafe(
  1163. this->CTest->GetCTestConfiguration("DropSitePassword")) +
  1164. "@" + this->CTest->GetCTestConfiguration("DropSite") +
  1165. cmCTest::MakeURLSafe(this->CTest->GetCTestConfiguration("DropLocation"));
  1166. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1167. cmCTestOptionalLog(
  1168. this->CTest, HANDLER_OUTPUT,
  1169. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(),
  1170. this->Quiet);
  1171. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  1172. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******",
  1173. this->Quiet);
  1174. }
  1175. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet);
  1176. }
  1177. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1178. this->CTest->GetCTestConfiguration("DropSite")
  1179. << this->CTest->GetCTestConfiguration("DropLocation")
  1180. << std::endl,
  1181. this->Quiet);
  1182. if (!this->SubmitUsingFTP(buildDirectory + "/Testing/" +
  1183. this->CTest->GetCurrentTag(),
  1184. files, prefix, url)) {
  1185. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1186. " Problems when submitting via FTP" << std::endl);
  1187. ofs << " Problems when submitting via FTP" << std::endl;
  1188. return -1;
  1189. }
  1190. if (!this->CDash) {
  1191. cmCTestOptionalLog(
  1192. this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  1193. << std::endl
  1194. << " Trigger site: "
  1195. << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl,
  1196. this->Quiet);
  1197. if (!this->TriggerUsingHTTP(
  1198. files, prefix,
  1199. this->CTest->GetCTestConfiguration("TriggerSite"))) {
  1200. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1201. " Problems when triggering via HTTP" << std::endl);
  1202. ofs << " Problems when triggering via HTTP" << std::endl;
  1203. return -1;
  1204. }
  1205. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1206. " Submission successful" << std::endl, this->Quiet);
  1207. ofs << " Submission successful" << std::endl;
  1208. return 0;
  1209. }
  1210. } else if (dropMethod == "http" || dropMethod == "https") {
  1211. std::string url = dropMethod;
  1212. url += "://";
  1213. ofs << "Using drop method: " << dropMethod << std::endl;
  1214. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1215. " Using HTTP submit method" << std::endl
  1216. << " Drop site:" << url,
  1217. this->Quiet);
  1218. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1219. url += this->CTest->GetCTestConfiguration("DropSiteUser");
  1220. cmCTestOptionalLog(
  1221. this->CTest, HANDLER_OUTPUT,
  1222. this->CTest->GetCTestConfiguration("DropSiteUser").c_str(),
  1223. this->Quiet);
  1224. if (!this->CTest->GetCTestConfiguration("DropSitePassword").empty()) {
  1225. url += ":" + this->CTest->GetCTestConfiguration("DropSitePassword");
  1226. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, ":******",
  1227. this->Quiet);
  1228. }
  1229. url += "@";
  1230. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, "@", this->Quiet);
  1231. }
  1232. url += this->CTest->GetCTestConfiguration("DropSite") +
  1233. this->CTest->GetCTestConfiguration("DropLocation");
  1234. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1235. this->CTest->GetCTestConfiguration("DropSite")
  1236. << this->CTest->GetCTestConfiguration("DropLocation")
  1237. << std::endl,
  1238. this->Quiet);
  1239. if (!this->SubmitUsingHTTP(buildDirectory + "/Testing/" +
  1240. this->CTest->GetCurrentTag(),
  1241. files, prefix, url)) {
  1242. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1243. " Problems when submitting via HTTP" << std::endl);
  1244. ofs << " Problems when submitting via HTTP" << std::endl;
  1245. return -1;
  1246. }
  1247. if (!this->CDash) {
  1248. cmCTestOptionalLog(
  1249. this->CTest, HANDLER_OUTPUT, " Using HTTP trigger method"
  1250. << std::endl
  1251. << " Trigger site: "
  1252. << this->CTest->GetCTestConfiguration("TriggerSite") << std::endl,
  1253. this->Quiet);
  1254. if (!this->TriggerUsingHTTP(
  1255. files, prefix,
  1256. this->CTest->GetCTestConfiguration("TriggerSite"))) {
  1257. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1258. " Problems when triggering via HTTP" << std::endl);
  1259. ofs << " Problems when triggering via HTTP" << std::endl;
  1260. return -1;
  1261. }
  1262. }
  1263. if (this->HasErrors) {
  1264. cmCTestLog(this->CTest, HANDLER_OUTPUT, " Errors occurred during "
  1265. "submission."
  1266. << std::endl);
  1267. ofs << " Errors occurred during submission. " << std::endl;
  1268. } else {
  1269. cmCTestOptionalLog(
  1270. this->CTest, HANDLER_OUTPUT, " Submission successful"
  1271. << (this->HasWarnings ? ", with warnings." : "") << std::endl,
  1272. this->Quiet);
  1273. ofs << " Submission successful"
  1274. << (this->HasWarnings ? ", with warnings." : "") << std::endl;
  1275. }
  1276. return 0;
  1277. } else if (dropMethod == "xmlrpc") {
  1278. #if defined(CTEST_USE_XMLRPC)
  1279. ofs << "Using drop method: XML-RPC" << std::endl;
  1280. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1281. " Using XML-RPC submit method" << std::endl,
  1282. this->Quiet);
  1283. std::string url = this->CTest->GetCTestConfiguration("DropSite");
  1284. prefix = this->CTest->GetCTestConfiguration("DropLocation");
  1285. if (!this->SubmitUsingXMLRPC(buildDirectory + "/Testing/" +
  1286. this->CTest->GetCurrentTag(),
  1287. files, prefix, url)) {
  1288. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1289. " Problems when submitting via XML-RPC" << std::endl);
  1290. ofs << " Problems when submitting via XML-RPC" << std::endl;
  1291. return -1;
  1292. }
  1293. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1294. " Submission successful" << std::endl, this->Quiet);
  1295. ofs << " Submission successful" << std::endl;
  1296. return 0;
  1297. #else
  1298. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1299. " Submission method \"xmlrpc\" not compiled into CTest!"
  1300. << std::endl);
  1301. return -1;
  1302. #endif
  1303. } else if (dropMethod == "scp") {
  1304. std::string url;
  1305. std::string oldWorkingDirectory;
  1306. if (!this->CTest->GetCTestConfiguration("DropSiteUser").empty()) {
  1307. url += this->CTest->GetCTestConfiguration("DropSiteUser") + "@";
  1308. }
  1309. url += this->CTest->GetCTestConfiguration("DropSite") + ":" +
  1310. this->CTest->GetCTestConfiguration("DropLocation");
  1311. // change to the build directory so that we can uses a relative path
  1312. // on windows since scp dosn't support "c:" a drive in the path
  1313. oldWorkingDirectory = cmSystemTools::GetCurrentWorkingDirectory();
  1314. cmSystemTools::ChangeDirectory(buildDirectory);
  1315. if (!this->SubmitUsingSCP(this->CTest->GetCTestConfiguration("ScpCommand"),
  1316. "Testing/" + this->CTest->GetCurrentTag(), files,
  1317. prefix, url)) {
  1318. cmSystemTools::ChangeDirectory(oldWorkingDirectory);
  1319. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1320. " Problems when submitting via SCP" << std::endl);
  1321. ofs << " Problems when submitting via SCP" << std::endl;
  1322. return -1;
  1323. }
  1324. cmSystemTools::ChangeDirectory(oldWorkingDirectory);
  1325. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1326. " Submission successful" << std::endl, this->Quiet);
  1327. ofs << " Submission successful" << std::endl;
  1328. return 0;
  1329. } else if (dropMethod == "cp") {
  1330. std::string location = this->CTest->GetCTestConfiguration("DropLocation");
  1331. // change to the build directory so that we can uses a relative path
  1332. // on windows since scp dosn't support "c:" a drive in the path
  1333. std::string oldWorkingDirectory =
  1334. cmSystemTools::GetCurrentWorkingDirectory();
  1335. cmSystemTools::ChangeDirectory(buildDirectory);
  1336. cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
  1337. " Change directory: " << buildDirectory << std::endl,
  1338. this->Quiet);
  1339. if (!this->SubmitUsingCP("Testing/" + this->CTest->GetCurrentTag(), files,
  1340. prefix, location)) {
  1341. cmSystemTools::ChangeDirectory(oldWorkingDirectory);
  1342. cmCTestLog(this->CTest, ERROR_MESSAGE,
  1343. " Problems when submitting via CP" << std::endl);
  1344. ofs << " Problems when submitting via cp" << std::endl;
  1345. return -1;
  1346. }
  1347. cmSystemTools::ChangeDirectory(oldWorkingDirectory);
  1348. cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT,
  1349. " Submission successful" << std::endl, this->Quiet);
  1350. ofs << " Submission successful" << std::endl;
  1351. return 0;
  1352. }
  1353. cmCTestLog(this->CTest, ERROR_MESSAGE, " Unknown submission method: \""
  1354. << dropMethod << "\"" << std::endl);
  1355. return -1;
  1356. }
  1357. std::string cmCTestSubmitHandler::GetSubmitResultsPrefix()
  1358. {
  1359. std::string buildname =
  1360. cmCTest::SafeBuildIdField(this->CTest->GetCTestConfiguration("BuildName"));
  1361. std::string name = this->CTest->GetCTestConfiguration("Site") + "___" +
  1362. buildname + "___" + this->CTest->GetCurrentTag() + "-" +
  1363. this->CTest->GetTestModelString() + "___XML___";
  1364. return name;
  1365. }
  1366. void cmCTestSubmitHandler::SelectParts(std::set<cmCTest::Part> const& parts)
  1367. {
  1368. // Check whether each part is selected.
  1369. for (cmCTest::Part p = cmCTest::PartStart; p != cmCTest::PartCount;
  1370. p = cmCTest::Part(p + 1)) {
  1371. this->SubmitPart[p] =
  1372. (std::set<cmCTest::Part>::const_iterator(parts.find(p)) != parts.end());
  1373. }
  1374. }
  1375. void cmCTestSubmitHandler::SelectFiles(cmCTest::SetOfStrings const& files)
  1376. {
  1377. this->Files.insert(files.begin(), files.end());
  1378. }