PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/fastback-0.4/fastback.cpp

#
C++ | 942 lines | 704 code | 139 blank | 99 comment | 123 complexity | f6bf16f1f709a2a2e93b75ab5e184e15 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. fastback.cpp
  3. Copyright (C) 2009 Gavin Romig-Koch <gavin@redhat.com>
  4. Copyright (C) 2009 Red Hat Inc.
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License along
  14. with this program; if not, write to the Free Software Foundation, Inc.,
  15. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. */
  17. /*
  18. Fastback uploads a file to a pre-configurable place. Fastback can be
  19. configured to send files (core files, log files, sos reports, etc) to
  20. places like your company's helpdesk, or support provider so that when
  21. something goes wrong you can concentrate on sending the file, not on
  22. remembering where or how to send the file. Fastback can also be included
  23. in automated scripts for delivering files.
  24. The file is named on the command line.
  25. An option to the command will cause the file to be encrypted before upload, and the output of the command will include the encryption key used.
  26. An option to the command will allow the user to associate a ticket name/number with the file.
  27. $ fastback FILE [ -t TICKET | -n ] [ -e ]
  28. where FILE is the file to be uploaded; where -e indicates that the file should be encrypted before it's uploaded; where -n indicates a new ticket; where TICKET is the name of the ticket
  29. If -t is not specified, -n is assumed.
  30. The name of the uploaded file will be the FILE name, prefixed with
  31. TICKET, an underscore, a short string of random characters, and a
  32. second underscore. The random characters are added to improve the
  33. filename's uniqueness. If TICKET is unspecified, or empty, it is
  34. replaced in the uploaded filename with "FB".
  35. The file will be compressed if it is not already compressed.
  36. The configuration file is called fastback.conf.
  37. The program keeps a log of all files uploaded, where they were uploaded to, encryption key used (if any), and a log of the messages from the transfer.
  38. */
  39. // $ fastback FILE [ -t TICKET | -n ] [ -e ]
  40. // where FILE is the file to be uploaded
  41. // where -e indicates that the file should be encrypted
  42. // where -n indicates a new ticket
  43. // where TICKET is the name of the ticket
  44. //
  45. // If -t is not specified, -n is assumed.
  46. //
  47. #ifndef _GNU_SOURCE
  48. #define _GNU_SOURCE
  49. #endif
  50. #include <string>
  51. #include <fstream>
  52. #include <sstream>
  53. #include <ext/stdio_filebuf.h>
  54. #include <iostream>
  55. #include <sys/types.h>
  56. #include <regex.h>
  57. #include <stdlib.h>
  58. #include <string.h>
  59. #include <argp.h>
  60. #include <curl/curl.h>
  61. #include <openssl/rand.h>
  62. typedef std::string string;
  63. static const char fastback_name[] = "fastback";
  64. static const char fastback_config_file[] = "/etc/fastback.conf";
  65. // These are command line options
  66. // if set they must be malloc'ed memory.
  67. static char* fastback_filename = 0; // local file to be uploaded
  68. static char* fastback_ticket = 0; // ticket to upload to
  69. static char* fastback_URLDIR = 0;
  70. static char* fastback_conf_LOGFILE = 0;
  71. static bool fastback_encrypt = false; // encrypt file before upload
  72. // '-n' option explicitly set
  73. static bool fastback_newticket = false;
  74. static bool fastback_verbose = false;
  75. static bool fastback_use_curl_loghandler = false;
  76. static FILE* fastback_logfile = 0;
  77. // if we need to create temporary files
  78. // first create a temporary directory, stick it's name here
  79. // and delete the whole tree when we are done
  80. static string fastback_tmpdir;
  81. static char* fastback_ftp_store_response = 0;
  82. static void translate(std::string& s, char in, char out)
  83. {
  84. // replace all the 'in' characters in 's' with 'out' characters
  85. size_t pos = 0;
  86. while ( (pos = s.find_first_of(in,pos)) != std::string::npos )
  87. s.replace(pos,1,1,out);
  88. }
  89. void
  90. Error(string func, string msg)
  91. {
  92. std::cerr << func << msg << std::endl;
  93. exit(4);
  94. }
  95. static size_t
  96. fastback_read(void *buffer, size_t size, size_t nmemb, void *userp)
  97. {
  98. return fread(buffer, size, nmemb, (FILE*)userp);
  99. }
  100. static int
  101. fastback_loghandler (CURL* handle, curl_infotype infotype,
  102. char* buffer, size_t buflen, void* vdebugdata)
  103. {
  104. // if the buffer ends in MSDOS style CR LF (\r \n) (FTP protocol does this),
  105. // convert it to just
  106. if (buflen >= 2
  107. && buffer[buflen-2] == '\r'
  108. && buffer[buflen-1] == '\n')
  109. {
  110. buffer[buflen-2] = '\n';
  111. buflen--;
  112. }
  113. // watch for interesting messages and responses in the headers
  114. static bool saw_store = false;
  115. if (saw_store)
  116. {
  117. if (infotype == CURLINFO_HEADER_IN)
  118. {
  119. fastback_ftp_store_response = (char*)malloc(buflen + 1);
  120. memcpy(fastback_ftp_store_response,buffer,buflen);
  121. fastback_ftp_store_response[buflen] = 0;
  122. }
  123. saw_store = false;
  124. }
  125. if (infotype == CURLINFO_HEADER_OUT)
  126. {
  127. if (buflen >= 5 && memcmp(buffer,"STOR ",5) == 0)
  128. {
  129. if (fastback_ftp_store_response)
  130. {
  131. free(fastback_ftp_store_response);
  132. fastback_ftp_store_response = 0;
  133. }
  134. saw_store = true;
  135. }
  136. }
  137. // print out the messages to the log file just as libcurl would
  138. // or as much like as is reasonable
  139. const char* type_string;
  140. FILE* fdebugdata = (FILE*)vdebugdata;
  141. bool dontprint = false;
  142. switch (infotype) {
  143. case CURLINFO_TEXT:
  144. type_string = "* ";
  145. break;
  146. case CURLINFO_HEADER_IN:
  147. type_string = "< ";
  148. break;
  149. case CURLINFO_HEADER_OUT:
  150. type_string = "> ";
  151. break;
  152. case CURLINFO_DATA_IN:
  153. type_string = "+ ";
  154. dontprint = true;
  155. break;
  156. case CURLINFO_DATA_OUT:
  157. type_string = "- ";
  158. dontprint = true;
  159. break;
  160. }
  161. if (!dontprint)
  162. {
  163. int l = strlen(type_string);
  164. if ( fwrite( type_string, 1, l, fdebugdata) != l )
  165. Error("fastback_loghandler", "error: could not write to logfile");
  166. if ( fwrite( buffer, 1, buflen, fdebugdata) != buflen )
  167. Error("fastback_loghandler", "error: could not write to logfile");
  168. }
  169. return 0;
  170. }
  171. static void
  172. show(FILE* file, string url)
  173. {
  174. if (fastback_URLDIR)
  175. fprintf(file,"# config URLDIR: %s\n", fastback_URLDIR);
  176. else
  177. fprintf(file,"# config URLDIR not set\n");
  178. if (fastback_conf_LOGFILE)
  179. fprintf(file,"# config LOGFILE: %s\n", fastback_conf_LOGFILE);
  180. else
  181. fprintf(file,"# config LOGFILE not set\n");
  182. if (fastback_filename)
  183. fprintf(file,"# arg file: %s\n", fastback_filename);
  184. if (fastback_ticket)
  185. fprintf(file,"# arg ticket: %s\n", fastback_ticket);
  186. else
  187. {
  188. if (fastback_newticket)
  189. fprintf(file,"# arg create new ticket\n");
  190. else
  191. fprintf(file,"# default create new ticket\n");
  192. }
  193. if (fastback_encrypt)
  194. fprintf(file,"# arg encrypt file\n");
  195. else
  196. fprintf(file,"# default don't encrypt file\n");
  197. fprintf(file,"# upload URL: %s\n", url.c_str());
  198. }
  199. static void
  200. print_curl_error(CURLcode err, string url, const char* msg)
  201. {
  202. if (err)
  203. {
  204. show(stderr, url);
  205. string tmsg = fastback_name;
  206. tmsg += ": error: ";
  207. tmsg += msg;
  208. tmsg += ": ";
  209. tmsg += curl_easy_strerror(err);
  210. tmsg += "\n";
  211. fprintf(stderr,tmsg.c_str());
  212. }
  213. }
  214. static void
  215. check_curl_error(CURLcode err, string url, const char* msg)
  216. {
  217. if (err)
  218. {
  219. print_curl_error(err,url,msg);
  220. exit(4);
  221. }
  222. }
  223. static void
  224. check_curl_perform_error(CURLcode err, string url, const char* msg)
  225. {
  226. if (err)
  227. {
  228. print_curl_error(err,url,msg);
  229. if (fastback_ftp_store_response)
  230. fprintf(stderr,"FTP STORE returned: %s", fastback_ftp_store_response);
  231. exit(4);
  232. }
  233. }
  234. static error_t
  235. fastback_argp_parser (int key, char *arg, struct argp_state *state)
  236. {
  237. switch (key)
  238. {
  239. case ARGP_KEY_ARG:
  240. if (fastback_filename)
  241. argp_error(state,"multiple FILE arguments specified");
  242. fastback_filename = strdup(arg);
  243. break;
  244. case 'e':
  245. fastback_encrypt = true;
  246. break;
  247. case 'n':
  248. if (fastback_ticket)
  249. argp_error(state, "invalid options: -n and -t conflict");
  250. fastback_newticket = true;
  251. break;
  252. case 't':
  253. if (fastback_newticket)
  254. argp_error(state, "invalid options: -n and -t conflict");
  255. else
  256. fastback_ticket = strdup(arg);
  257. break;
  258. case 'v':
  259. fastback_verbose = true;
  260. break;
  261. case 1001:
  262. fastback_use_curl_loghandler = true;
  263. break;
  264. default:
  265. return ARGP_ERR_UNKNOWN;
  266. }
  267. return 0;
  268. }
  269. static struct argp_option fastback_options[] = {
  270. {"ticket",'t', "TICKET", 0, "the ticket to associate FILE with"},
  271. {0,'n',0,0,"create a new ticket for FILE"},
  272. {"encrypt",'e',0,0,"encrypt FILE before uploading"},
  273. {0,'v',0,0,"be verbose"},
  274. {"debug-use-curl-loghandler",1001,0,0,"use curl's log handler"},
  275. { 0 }};
  276. static struct argp fastback_argp = {
  277. fastback_options,
  278. fastback_argp_parser,
  279. "FILE"
  280. };
  281. struct config_option {
  282. const char* option;
  283. char** storage;
  284. };
  285. config_option all_config_options[] = {
  286. { "URLDIR", &fastback_URLDIR },
  287. { "LOGFILE", &fastback_conf_LOGFILE },
  288. { 0, 0 }
  289. };
  290. static bool
  291. config_line_parse(const char* filename,
  292. const char* line,
  293. size_t var_start, size_t var_end,
  294. size_t value_start, size_t value_end)
  295. {
  296. config_option* each;
  297. for (each = all_config_options; each->option; each++)
  298. {
  299. size_t option_length = strlen(each->option);
  300. if (option_length == (var_end - var_start)
  301. && memcmp(each->option,line+var_start,option_length) == 0)
  302. {
  303. int value_length = value_end - value_start;
  304. (*each->storage) = (char*)malloc(value_length + 1);
  305. memcpy((*each->storage), line+value_start, value_length);
  306. (*each->storage)[value_length] = 0;
  307. return false;
  308. }
  309. }
  310. {
  311. int var_length = var_end - var_start;
  312. char* var = (char*)malloc(var_length + 1);
  313. memcpy(var, line+var_start, var_length);
  314. var[var_length] = 0;
  315. fprintf(stderr,"%s: %s\n", filename, line);
  316. fprintf(stderr, "%s: error: unknown config option: %s\n",
  317. fastback_name, var);
  318. free(var);
  319. return true;
  320. }
  321. }
  322. static void
  323. error_regerror (int errcode, regex_t *compiled, const char* func)
  324. {
  325. size_t length = regerror (errcode, compiled, NULL, 0);
  326. char *buffer = (char*)malloc (length);
  327. (void) regerror (errcode, compiled, buffer, length);
  328. fprintf(stderr, "%s: error: %s: %s\n", fastback_name, func, buffer);
  329. free(buffer);
  330. }
  331. static bool
  332. parse_config(const char* filename)
  333. {
  334. int config_file_error = 0;
  335. int err;
  336. regex_t regexp;
  337. FILE* file;
  338. char *line;
  339. size_t linesize;
  340. ssize_t readcount;
  341. const char pattern[] =
  342. "^[[:space:]]*"
  343. "\\(\\|#.*\\|"
  344. "\\([[:alpha:]][[:alnum:]]*\\)[[:space:]]*="
  345. "[[:space:]]*\\(\"\\([^\"]*\\)\"\\|\\([^[:space:]]*\\)\\)[[:space:]]*"
  346. "\\)$";
  347. const bool debug_pattern = false;
  348. err = regcomp( &regexp, pattern, 0);
  349. if (err)
  350. {
  351. error_regerror(err, &regexp, "regcomp");
  352. exit(4);
  353. }
  354. size_t matchsize = regexp.re_nsub + 1;
  355. regmatch_t* matchptr = (regmatch_t*)malloc(sizeof(regmatch_t) * matchsize);
  356. file = fopen(filename,"r");
  357. if (!file)
  358. {
  359. string msg = fastback_name;
  360. msg += ": error: could not open: ";
  361. msg += filename;
  362. perror(msg.c_str());
  363. exit(4);
  364. }
  365. line = 0;
  366. linesize = 0;
  367. readcount = getline(&line, &linesize, file);
  368. while (readcount != -1)
  369. {
  370. /* trim the newline */
  371. if (readcount != 0 && line[readcount-1] == '\n')
  372. line[readcount-1] = 0;
  373. err = regexec( &regexp, line, matchsize, matchptr, 0);
  374. if (err == 0)
  375. {
  376. if (debug_pattern)
  377. {
  378. int i;
  379. fprintf(stderr,"%s: debug: line: %s: %s\n",
  380. fastback_name, filename, line);
  381. for (i=0; i < matchsize; i++)
  382. if (matchptr[i].rm_so == -1)
  383. fprintf(stderr,"%s: debug: match (%d): no match\n",
  384. fastback_name, i);
  385. else
  386. {
  387. int j;
  388. char buf[1000];
  389. for(j=0; j < (matchptr[i].rm_eo - matchptr[i].rm_so); j++)
  390. buf[j] = line[matchptr[i].rm_so + j];
  391. buf[j] = 0;
  392. if (j >= 1000)
  393. {
  394. fprintf(stderr,"INTERNAL ERROR: J too large\n");
  395. exit(4);
  396. }
  397. fprintf(stderr, "%s: debug: match (%d): \"%s\"\n",
  398. fastback_name, i, buf);
  399. }
  400. }
  401. if (matchptr[2].rm_so != -1)
  402. {
  403. if (matchptr[4].rm_so != -1)
  404. config_file_error |=
  405. config_line_parse(filename, line,
  406. matchptr[2].rm_so, matchptr[2].rm_eo,
  407. matchptr[4].rm_so, matchptr[4].rm_eo);
  408. else if (matchptr[5].rm_so != -1)
  409. config_file_error |=
  410. config_line_parse(filename, line,
  411. matchptr[2].rm_so, matchptr[2].rm_eo,
  412. matchptr[5].rm_so, matchptr[5].rm_eo);
  413. }
  414. }
  415. else if (err == REG_NOMATCH)
  416. {
  417. fprintf(stderr,"%s: %s\n", filename, line);
  418. fprintf(stderr,"%s: error: invalid config line\n", fastback_name);
  419. config_file_error = true;
  420. }
  421. else
  422. {
  423. fprintf(stderr,"%s: %s\n", filename, line);
  424. error_regerror(err, &regexp, "regexe");
  425. exit(4);
  426. }
  427. readcount = getline(&line, &linesize, file);
  428. }
  429. fclose(file);
  430. return config_file_error;
  431. }
  432. void RunCommand(string cmd)
  433. {
  434. int retcode = system(cmd.c_str());
  435. if (retcode == -1)
  436. {
  437. Error("RunCommand:", "error: could not start subshell: " + cmd);
  438. }
  439. if (retcode)
  440. {
  441. std::ostringstream msg;
  442. msg << "error: subshell failed (rc=" << retcode << "):" << cmd;
  443. Error("RunCommand:", msg.str());
  444. }
  445. }
  446. string ReadCommand(string cmd)
  447. {
  448. FILE* fp = popen(cmd.c_str(),"r");
  449. if (!fp)
  450. {
  451. Error("ReadCommand:", "error: could not start subshell: " + cmd);
  452. }
  453. __gnu_cxx::stdio_filebuf<char> command_output_buffer(fp, std::ios_base::in);
  454. std::ostringstream output_stream;
  455. output_stream << &command_output_buffer;
  456. int retcode = pclose(fp);
  457. if (retcode)
  458. {
  459. std::ostringstream msg;
  460. msg << "error: subshell failed (rc=" << retcode << "):" << cmd;
  461. Error("ReadCommand:", msg.str());
  462. }
  463. return output_stream.str();
  464. }
  465. void WriteCommand(string cmd, string input)
  466. {
  467. FILE* fp = popen(cmd.c_str(),"w");
  468. if (!fp)
  469. {
  470. Error("WriteCommand:", "error: could not start subshell: " + cmd);
  471. }
  472. size_t input_length = input.length();
  473. size_t check = fwrite(input.c_str(),1,input_length,fp);
  474. if (input_length != check)
  475. {
  476. Error("WriteCommand:", "error: could not send input to subshell: " + cmd);
  477. }
  478. int retcode = pclose(fp);
  479. if (retcode)
  480. {
  481. std::ostringstream msg;
  482. msg << "error: subshell failed (rc=" << retcode << "):" << cmd;
  483. Error("WriteCommand:", msg.str());
  484. }
  485. }
  486. static string rand_base64(int i)
  487. {
  488. // return a string of random base64 characters of 'i' length
  489. //
  490. // This should probably use the openssl library (or some other
  491. // library) directly, but it doesn't right now
  492. std::ostringstream cmd;
  493. cmd << "openssl rand -base64 " << i;
  494. string r = ReadCommand(cmd.str());
  495. return r.substr(0,r.length()-1);
  496. }
  497. static string rand_filename(int i)
  498. {
  499. // return a string of length i of random characters suitable for
  500. // use as part of a filename basename (no slashes). Base64 will work
  501. // fine except that the '/' needs to be changed to something else,
  502. // '-' in this case.
  503. string s = rand_base64(i);
  504. translate(s,'/','-');
  505. return s;
  506. }
  507. static string
  508. remote_filename(const string& filename, const string& ticket)
  509. {
  510. // create a remote filename from the local file's basename.
  511. // If the ticket name is not empty, start with that and an '_'.
  512. // else start with 'FB_'
  513. // Add 24 random bits encoded as base64 for filenames, and
  514. // a second '_'.
  515. const int max_rand_bytes = 3; // 24 random bits
  516. string r = rand_filename(max_rand_bytes);
  517. if ( ticket.length() == 0 )
  518. return string("FB_") + r + '_' + filename;
  519. else
  520. return ticket + '_' + r + '_' + filename;
  521. }
  522. static bool
  523. is_compressed(const string& filename)
  524. {
  525. string cmd = string("file ") + filename;
  526. string output = ReadCommand(cmd);
  527. return output.find("compressed") != string::npos;
  528. }
  529. static void
  530. compress(const string& in_filename, const string& out_filename)
  531. {
  532. // compress file 'in_filename' into 'out_filename'
  533. // again this should probably use a library, but doesn't yet
  534. string cmd = string("gzip <") + in_filename + " >" + out_filename;
  535. ReadCommand(cmd);
  536. }
  537. static bool
  538. readable(string filename)
  539. {
  540. // is 'filename' readable by the running user
  541. FILE* file = fopen(filename.c_str(),"r");
  542. if (file)
  543. {
  544. fclose(file);
  545. return true;
  546. }
  547. return false;
  548. }
  549. static string
  550. filename_basename(const string& filename)
  551. {
  552. string::size_type p = filename.find_last_of('/');
  553. if (p == string::npos)
  554. return filename;
  555. else
  556. return filename.substr(p+1,string::npos);
  557. }
  558. static string
  559. filename_dirname(const string& filename)
  560. {
  561. string::size_type p = filename.find_last_of('/');
  562. if (p == string::npos)
  563. return ".";
  564. else
  565. return filename.substr(0,p);
  566. }
  567. static string
  568. filename_temporary(string filename)
  569. {
  570. // ensure that a temporary directory (for this run) exists
  571. // 'filename' must be the basename of a file
  572. // 'filename' must be unique to this run
  573. if (fastback_tmpdir == "")
  574. {
  575. char TEMPLATE[] = "/tmp/fastbackXXXXXX";
  576. fastback_tmpdir = mkdtemp(TEMPLATE);
  577. }
  578. return fastback_tmpdir + '/' + filename;
  579. }
  580. static void
  581. cleanup()
  582. {
  583. free(fastback_filename);
  584. free(fastback_ticket);
  585. free(fastback_URLDIR);
  586. free(fastback_conf_LOGFILE);
  587. free(fastback_ftp_store_response);
  588. if (fastback_tmpdir != "")
  589. RunCommand(string("rm -rf " + fastback_tmpdir));
  590. if (fastback_logfile)
  591. fclose(fastback_logfile);
  592. }
  593. int
  594. main(int argc, char** argv)
  595. {
  596. error_t err;
  597. bool use_scp_command = false;
  598. err = argp_parse( &fastback_argp, argc, argv, 0, 0, 0);
  599. if (err)
  600. {
  601. if (errno == err)
  602. {
  603. string msg = fastback_name;
  604. msg += ": error: argp_parse";
  605. perror(msg.c_str());
  606. }
  607. else
  608. fprintf(stderr, "%s: error from argp_parse: error code %d\n",
  609. fastback_name, err);
  610. cleanup();
  611. exit(2);
  612. }
  613. if (!fastback_filename)
  614. {
  615. fprintf(stderr, "%s: error: no FILE given on command line\n",
  616. fastback_name);
  617. fprintf(stderr,
  618. "Try `%s --help' or `fastback --usage' for more information.\n",
  619. fastback_name);
  620. exit(2);
  621. }
  622. if (parse_config(fastback_config_file))
  623. exit(2);
  624. if (fastback_conf_LOGFILE)
  625. {
  626. fastback_logfile = fopen(fastback_conf_LOGFILE,"a");
  627. if (!fastback_logfile)
  628. {
  629. string msg = fastback_name;
  630. msg += ": error: could not open logfile: ";
  631. msg += fastback_conf_LOGFILE;
  632. perror(msg.c_str());
  633. exit(3);
  634. }
  635. }
  636. if (!fastback_URLDIR || !strcmp(fastback_URLDIR,""))
  637. {
  638. fprintf(stderr,
  639. "%s: error: URLDIR not set in: %s\n",
  640. fastback_name, fastback_config_file);
  641. exit(3);
  642. }
  643. if (!readable(fastback_filename))
  644. {
  645. string msg = fastback_name;
  646. msg += ": error: could not read: ";
  647. msg += fastback_filename;
  648. perror(msg.c_str());
  649. exit(3);
  650. }
  651. string outfile_name = fastback_filename;
  652. // compress the file if it isn't already
  653. if (!is_compressed(outfile_name))
  654. {
  655. string compressed_filename =
  656. filename_temporary(filename_basename(outfile_name) + ".gz");
  657. compress(outfile_name,compressed_filename);
  658. outfile_name = compressed_filename;
  659. }
  660. // encrypt if requested
  661. string key;
  662. if (fastback_encrypt)
  663. {
  664. string cmd = string("openssl rand -base64 48");
  665. key = ReadCommand(cmd);
  666. string infile_name = outfile_name;
  667. outfile_name =
  668. filename_temporary(filename_basename(outfile_name) + ".aes");
  669. cmd = string("openssl aes-128-cbc -in ") + infile_name +
  670. " -out " + outfile_name + " -pass stdin";
  671. WriteCommand(cmd,key);
  672. }
  673. // generate md5sum
  674. string cmd = string("md5sum ") + outfile_name;
  675. string md5sum = ReadCommand(cmd);
  676. // randomize the remote file name
  677. // and alter the md5sum output to contain the remote file name
  678. string remotefile_name =
  679. remote_filename(filename_basename(outfile_name.c_str()),
  680. (fastback_ticket == 0)
  681. ? string("") : string(fastback_ticket));
  682. md5sum = md5sum.substr(0,md5sum.find_first_of(' ')) + " " + remotefile_name + '\n';
  683. // create the full remote file name, 'url'
  684. string url = fastback_URLDIR;
  685. if (url[url.length()-1] != '/')
  686. url += '/';
  687. url += remotefile_name;
  688. if (fastback_verbose)
  689. show(stdout,url);
  690. if (fastback_logfile)
  691. show(fastback_logfile,url);
  692. if (url.substr(0,4) == "scp:")
  693. use_scp_command = true;
  694. if (use_scp_command)
  695. {
  696. if (url.substr(0,6) != "scp://")
  697. {
  698. fprintf(stderr,
  699. "%s: error: invalid scp URL, does not start with 'scp://': %s\n",
  700. fastback_name, url.c_str());
  701. exit(3);
  702. }
  703. size_t urlend = url.length();
  704. size_t hostend = url.find_first_of('/',6);
  705. string host = url.substr(6,hostend-6);
  706. const char* replacement;
  707. if (hostend+1 >= urlend || url[hostend+1] != '~')
  708. replacement = ":/";
  709. else
  710. replacement = ":";
  711. string cmd = string("scp ") + outfile_name + ' ' + host + replacement + url.substr(hostend+1);
  712. if (fastback_logfile)
  713. fprintf(fastback_logfile,"$ %s\n", cmd.c_str());
  714. RunCommand(cmd);
  715. }
  716. else
  717. {
  718. CURL* handle;
  719. CURLcode curl_err;
  720. FILE* file;
  721. file = fopen(outfile_name.c_str(),"r");
  722. if (!file)
  723. {
  724. string msg = fastback_name;
  725. msg += ": error: could not open: ";
  726. msg += outfile_name;
  727. perror(msg.c_str());
  728. exit(3);
  729. }
  730. if (curl_global_init(CURL_GLOBAL_ALL))
  731. {
  732. fprintf(stderr,"%s: error: curl_global_init: could not initialze curl\n",
  733. fastback_name);
  734. exit(3);
  735. }
  736. handle = curl_easy_init();
  737. if (!handle)
  738. {
  739. fprintf(stderr,"%s: error: curl_easy_init: could not initialize curl\n", fastback_name);
  740. exit(3);
  741. }
  742. if (fastback_logfile)
  743. {
  744. curl_err = curl_easy_setopt(handle, CURLOPT_VERBOSE, 1);
  745. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_VERBOSE)");
  746. curl_err = curl_easy_setopt(handle, CURLOPT_STDERR, fastback_logfile);
  747. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_STDERR)");
  748. if (!fastback_use_curl_loghandler)
  749. {
  750. curl_err = curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION,
  751. fastback_loghandler);
  752. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_DEBUGFUNCTION)");
  753. curl_err = curl_easy_setopt(handle, CURLOPT_DEBUGDATA, fastback_logfile);
  754. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_DEBUGDATA)");
  755. }
  756. }
  757. curl_err = curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
  758. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_URL)");
  759. curl_err = curl_easy_setopt(handle, CURLOPT_READFUNCTION, fastback_read);
  760. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_READFUNCTION)");
  761. curl_err = curl_easy_setopt(handle, CURLOPT_READDATA, (void*)file);
  762. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_READDATA)");
  763. curl_err = curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
  764. check_curl_error(curl_err, url, "curl_easy_setopt(CURLOPT_READDATA)");
  765. curl_err = curl_easy_perform(handle);
  766. check_curl_perform_error(curl_err, url, "curl_easy_perform");
  767. curl_easy_cleanup(handle);
  768. fclose(file);
  769. }
  770. if (fastback_ticket)
  771. printf("Please copy this into %s:\n", fastback_ticket);
  772. else
  773. printf("Please send this to your technical support:\n");
  774. printf("FASTBACK: This report was sent to %s\n", filename_dirname(url).c_str());
  775. if (fastback_ticket)
  776. printf("TICKET: %s\n", fastback_ticket);
  777. printf("FILE: %s\n", filename_basename(url).c_str());
  778. printf("MD5SUM:\n");
  779. printf("%s", md5sum.c_str());
  780. if (fastback_encrypt)
  781. {
  782. printf("KEY: aes-128-cbc\n");
  783. printf("%s", key.c_str());
  784. }
  785. printf("END:\n");
  786. cleanup();
  787. return 0;
  788. }