ocinternal.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
  2. See the COPYRIGHT file for more information. */
  3. #include "config.h"
  4. #include <stdio.h>
  5. #include <fcntl.h>
  6. #include <errno.h>
  7. #ifdef HAVE_UNISTD_H
  8. #include <unistd.h>
  9. #endif
  10. #include "ocinternal.h"
  11. #include "ocdebug.h"
  12. #include "ocdata.h"
  13. #include "occontent.h"
  14. #include "occlientparams.h"
  15. #include "ocrc.h"
  16. #include "occurlfunctions.h"
  17. #include "ochttp.h"
  18. #include "ocread.h"
  19. /* Note: TMPPATH must end in '/' */
  20. #ifdef __CYGWIN__
  21. #define TMPPATH1 "/cygdrive/c/temp/"
  22. #define TMPPATH2 "./"
  23. #elifdef WIN32
  24. #define TMPPATH1 "c:\\temp/"
  25. #define TMPPATH2 ".\\"
  26. #else
  27. #define TMPPATH1 "/tmp/"
  28. #define TMPPATH2 "./"
  29. #endif
  30. /* Define default rc files and aliases*/
  31. static char* rcfilenames[3] = {".dodsrc",".ocrc",NULL};
  32. static int ocextractddsinmemory(OCstate*,OCtree*,int);
  33. static int ocextractddsinfile(OCstate*,OCtree*,int);
  34. static char* constraintescape(const char* url);
  35. static OCerror createtempfile(OCstate*,OCtree*);
  36. static int createtempfile1(char*,char**);
  37. static void ocsetcurlproperties(OCstate*);
  38. extern OCnode* makeunlimiteddimension(void);
  39. #ifdef WIN32
  40. #include <fcntl.h>
  41. #define _S_IREAD 256
  42. #define _S_IWRITE 128
  43. #else
  44. #include <sys/stat.h>
  45. #endif
  46. /* Global flags*/
  47. int oc_curl_file_supported;
  48. int oc_curl_https_supported;
  49. int
  50. ocinternalinitialize(void)
  51. {
  52. int stat = OC_NOERR;
  53. /* Compute some xdr related flags */
  54. xxdr_init();
  55. oc_loginit();
  56. /* Determine if this version of curl supports
  57. "file://..." &/or "https://..." urls.
  58. */
  59. {
  60. const char* const* proto; /*weird*/
  61. curl_version_info_data* curldata;
  62. curldata = curl_version_info(CURLVERSION_NOW);
  63. oc_curl_file_supported = 0;
  64. oc_curl_https_supported = 0;
  65. for(proto=curldata->protocols;*proto;proto++) {
  66. if(strcmp("file",*proto)==0) {oc_curl_file_supported=1;break;}
  67. if(strcmp("https",*proto)==0) {oc_curl_https_supported=1;break;}
  68. }
  69. if(ocdebug > 0) {
  70. oc_log(LOGNOTE,"Curl file:// support = %d",oc_curl_file_supported);
  71. oc_log(LOGNOTE,"Curl https:// support = %d",oc_curl_file_supported);
  72. }
  73. }
  74. /* compile the .dodsrc, if any */
  75. {
  76. char* path = NULL;
  77. char* homepath = NULL;
  78. char** alias;
  79. FILE* f = NULL;
  80. /* locate the configuration files: . first in '.', then $HOME */
  81. for(alias=rcfilenames;*alias;alias++) {
  82. path = (char*)malloc(strlen("./")+strlen(*alias)+1);
  83. if(path == NULL) return OC_ENOMEM;
  84. strcpy(path,"./");
  85. strcat(path,*alias);
  86. /* see if file is readable */
  87. f = fopen(path,"r");
  88. if(f != NULL) break;
  89. if(path != NULL) {free(path); path = NULL;} /* cleanup */
  90. }
  91. if(f == NULL) { /* try $HOME */
  92. OCASSERT(path == NULL);
  93. homepath = getenv("HOME");
  94. if (homepath!= NULL) {
  95. for(alias=rcfilenames;*alias;alias++) {
  96. path = (char*)malloc(strlen(homepath)+1+strlen(*alias)+1);
  97. if(path == NULL) return OC_ENOMEM;
  98. strcpy(path,homepath);
  99. strcat(path,"/");
  100. strcat(path,*alias);
  101. f = fopen(path,"r");
  102. if(f != NULL) break;
  103. if(path != NULL) {free(path); path=NULL;}
  104. }
  105. }
  106. }
  107. if(f == NULL) {
  108. oc_log(LOGDBG,"Cannot find runtime configuration file");
  109. } else {
  110. OCASSERT(path != NULL);
  111. fclose(f);
  112. if(ocdebug > 1)
  113. fprintf(stderr, "DODS RC file: %s\n", path);
  114. if(ocdodsrc_read(*alias,path) == 0)
  115. oc_log(LOGERR, "Error parsing %s\n",path);
  116. }
  117. if(path != NULL) free(path);
  118. }
  119. return OCTHROW(stat);
  120. }
  121. /**************************************************/
  122. OCerror
  123. ocopen(OCstate** statep, const char* url)
  124. {
  125. int stat = OC_NOERR;
  126. OCstate * state = NULL;
  127. OCURI* tmpurl = NULL;
  128. CURL* curl = NULL; /* curl handle*/
  129. if(!ocuriparse(url,&tmpurl)) {OCTHROWCHK(stat=OC_EBADURL); goto fail;}
  130. stat = occurlopen(&curl);
  131. if(stat != OC_NOERR) {OCTHROWCHK(stat); goto fail;}
  132. state = (OCstate*)ocmalloc(sizeof(OCstate)); /* ocmalloc zeros memory*/
  133. if(state == NULL) {OCTHROWCHK(stat=OC_ENOMEM); goto fail;}
  134. /* Setup DAP state*/
  135. state->magic = OCMAGIC;
  136. state->curl = curl;
  137. state->trees = oclistnew();
  138. state->uri = tmpurl;
  139. if(!ocuridecodeparams(state->uri)) {
  140. oc_log(LOGWARN,"Could not parse client parameters");
  141. }
  142. state->packet = ocbytesnew();
  143. ocbytessetalloc(state->packet,DFALTPACKETSIZE); /*initial reasonable size*/
  144. /* set curl properties for this link */
  145. ocsetcurlproperties(state);
  146. /* Set up list to support reuse/reclamation of OCcontent objects. */
  147. state->contentlist = NULL;
  148. if(statep) *statep = state;
  149. return OCTHROW(stat);
  150. fail:
  151. ocurifree(tmpurl);
  152. if(state != NULL) ocfree(state);
  153. if(curl != NULL) occurlclose(curl);
  154. return OCTHROW(stat);
  155. }
  156. OCerror
  157. ocfetchf(OCstate* state, const char* constraint, OCdxd kind, OCflags flags,
  158. OCnode** rootp)
  159. {
  160. OCtree* tree = NULL;
  161. OCnode* root = NULL;
  162. OCerror stat = OC_NOERR;
  163. tree = (OCtree*)ocmalloc(sizeof(OCtree));
  164. MEMCHECK(tree,OC_ENOMEM);
  165. memset((void*)tree,0,sizeof(OCtree));
  166. tree->dxdclass = kind;
  167. tree->state = state;
  168. tree->constraint = constraintescape(constraint);
  169. if(tree->constraint == NULL)
  170. tree->constraint = nulldup(constraint);
  171. /* Set curl properties: pwd, flags, proxies, ssl */
  172. if((stat=ocset_user_password(state))!= OC_NOERR) goto fail;
  173. if((stat=ocset_curl_flags(state)) != OC_NOERR) goto fail;
  174. if((stat=ocset_proxy(state)) != OC_NOERR) goto fail;
  175. if((stat=ocset_ssl(state)) != OC_NOERR) goto fail;
  176. ocbytesclear(state->packet);
  177. switch (kind) {
  178. case OCDAS:
  179. stat = readDAS(state,tree);
  180. if(stat == OC_NOERR) {
  181. tree->text = ocbytesdup(state->packet);
  182. if(tree->text == NULL) stat = OC_EDAS;
  183. }
  184. break;
  185. case OCDDS:
  186. stat = readDDS(state,tree);
  187. if(stat == OC_NOERR) {
  188. tree->text = ocbytesdup(state->packet);
  189. if(tree->text == NULL) stat = OC_EDDS;
  190. }
  191. break;
  192. case OCDATADDS:
  193. if((flags & OCONDISK) != 0) {/* store in file */
  194. /* Create the datadds file immediately
  195. so that DRNO can reference it*/
  196. /* Make the tmp file*/
  197. stat = createtempfile(state,tree);
  198. if(stat) {OCTHROWCHK(stat); goto unwind;}
  199. stat = readDATADDS(state,tree,flags);
  200. if(stat == OC_NOERR) {
  201. /* Separate the DDS from data and return the dds;
  202. will modify packet */
  203. stat = ocextractddsinfile(state,tree,flags);
  204. }
  205. } else { /*in memory*/
  206. stat = readDATADDS(state,tree,flags);
  207. if(stat == OC_NOERR) {
  208. /* Separate the DDS from data and return the dds;
  209. will modify packet */
  210. stat = ocextractddsinmemory(state,tree,flags);
  211. }
  212. }
  213. break;
  214. }/*switch*/
  215. if(stat != OC_NOERR) {
  216. /* Obtain any http code */
  217. state->error.httpcode = ocfetchhttpcode(state->curl);
  218. if(state->error.httpcode >= 400) {
  219. oc_log(LOGWARN,"oc_open: Could not read url; http error = %l",state->error.httpcode);
  220. } else {
  221. oc_log(LOGWARN,"oc_open: Could not read url");
  222. }
  223. return OCTHROW(stat);
  224. }
  225. tree->nodes = NULL;
  226. stat = DAPparse(state,tree,tree->text);
  227. /* Check and report on an error return from the server */
  228. if(stat == OC_EDAPSVC && state->error.code != NULL) {
  229. oc_log(LOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
  230. state->error.code,
  231. (state->error.message?state->error.message:""));
  232. }
  233. if(stat) {OCTHROWCHK(stat); goto unwind;}
  234. root = tree->root;
  235. /* make sure */
  236. tree->root = root;
  237. root->tree = tree;
  238. /* Verify the parse */
  239. switch (kind) {
  240. case OCDAS:
  241. if(root->octype != OC_Attributeset)
  242. {OCTHROWCHK(stat=OC_EDAS); goto unwind;}
  243. break;
  244. case OCDDS:
  245. if(root->octype != OC_Dataset)
  246. {OCTHROWCHK(stat=OC_EDDS); goto unwind;}
  247. break;
  248. case OCDATADDS:
  249. if(root->octype != OC_Dataset)
  250. {OCTHROWCHK(stat=OC_EDATADDS); goto unwind;}
  251. /* Modify the tree kind */
  252. tree->dxdclass = OCDATADDS;
  253. break;
  254. default: return OC_EINVAL;
  255. }
  256. if(kind != OCDAS) {
  257. /* Process ocnodes to assign offsets and sizes where possible */
  258. occomputeskipdata(state,root);
  259. /* Process ocnodes to mark those that are cacheable */
  260. ocmarkcacheable(state,root);
  261. /* Process ocnodes to handle various semantic issues*/
  262. occomputesemantics(tree->nodes);
  263. }
  264. /* Process ocnodes to compute name info*/
  265. occomputefullnames(tree->root);
  266. if(kind == OCDATADDS) {
  267. if((flags & OCONDISK) != 0) {
  268. tree->data.xdrs = xxdr_filecreate(tree->data.file,tree->data.bod);
  269. } else {
  270. /* Switch to zero based memory */
  271. tree->data.xdrs
  272. = xxdr_memcreate(tree->data.memory,tree->data.datasize,tree->data.bod);
  273. }
  274. MEMCHECK(tree->data.xdrs,OC_ENOMEM);
  275. }
  276. /* Put root into the state->trees list */
  277. oclistpush(state->trees,(ocelem)root);
  278. if(rootp) *rootp = root;
  279. return stat;
  280. unwind:
  281. ocfreetree(tree);
  282. fail:
  283. return OCTHROW(stat);
  284. }
  285. void
  286. occlose(OCstate* state)
  287. {
  288. unsigned int i;
  289. if(state == NULL) return;
  290. /* Warning: ocfreeroot will attempt to remove the root from state->trees */
  291. /* Ok in this case because we are popping the root out of state->trees */
  292. for(i=0;i<oclistlength(state->trees);i++) {
  293. OCnode* root = (OCnode*)oclistpop(state->trees);
  294. ocfreeroot(root);
  295. }
  296. oclistfree(state->trees);
  297. ocurifree(state->uri);
  298. ocbytesfree(state->packet);
  299. ocfree(state->error.code);
  300. ocfree(state->error.message);
  301. if(state->contentlist != NULL) {
  302. struct OCcontent* next;
  303. struct OCcontent* curr = state->contentlist;
  304. while(curr != NULL) {
  305. next = curr->next;
  306. ocfree(curr);
  307. curr = next;
  308. }
  309. }
  310. ocfree(state->curlflags.useragent);
  311. ocfree(state->curlflags.cookiejar);
  312. ocfree(state->curlflags.cookiefile);
  313. ocfree(state->ssl.certificate);
  314. ocfree(state->ssl.key);
  315. ocfree(state->ssl.keypasswd);
  316. ocfree(state->ssl.cainfo);
  317. ocfree(state->ssl.capath);
  318. ocfree(state->proxy.host);
  319. ocfree(state->creds.username);
  320. ocfree(state->creds.password);
  321. if(state->curl != NULL) occurlclose(state->curl);
  322. ocfree(state);
  323. }
  324. static OCerror
  325. ocextractddsinmemory(OCstate* state, OCtree* tree, OCflags flags)
  326. {
  327. OCerror stat = OC_NOERR;
  328. size_t ddslen, bod, bodfound;
  329. /* Read until we find the separator (or EOF)*/
  330. bodfound = findbod(state->packet,&bod,&ddslen);
  331. if(!bodfound) {/* No BOD; pretend */
  332. bod = tree->data.bod;
  333. ddslen = tree->data.datasize;
  334. }
  335. tree->data.bod = bod;
  336. tree->data.ddslen = ddslen;
  337. /* copy out the dds */
  338. if(ddslen > 0) {
  339. tree->text = (char*)ocmalloc(ddslen+1);
  340. memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
  341. tree->text[ddslen] = '\0';
  342. } else
  343. tree->text = NULL;
  344. /* Extract the inmemory contents */
  345. tree->data.memory = ocbytesextract(state->packet);
  346. #ifdef OCIGNORE
  347. /* guarantee the data part is on an 8 byte boundary */
  348. if(tree->data.bod % 8 != 0) {
  349. unsigned long count = tree->data.datasize - tree->data.bod;
  350. memcpy(tree->xdrmemory,tree->xdrmemory+tree->data.bod,count);
  351. tree->data.datasize = count;
  352. tree->data.bod = 0;
  353. tree->data.ddslen = 0;
  354. }
  355. #endif
  356. if(tree->text == NULL) stat = OC_EDATADDS;
  357. return OCTHROW(stat);
  358. }
  359. static OCerror
  360. ocextractddsinfile(OCstate* state, OCtree* tree, OCflags flags)
  361. {
  362. OCerror stat = OC_NOERR;
  363. size_t ddslen, bod, bodfound;
  364. /* Read until we find the separator (or EOF)*/
  365. ocbytesclear(state->packet);
  366. rewind(tree->data.file);
  367. bodfound = 0;
  368. do {
  369. char chunk[1024];
  370. size_t count;
  371. /* read chunks of the file until we find the separator*/
  372. count = fread(chunk,1,sizeof(chunk),tree->data.file);
  373. if(count <= 0) break; /* EOF;*/
  374. ocbytesappendn(state->packet,chunk,count);
  375. bodfound = findbod(state->packet,&bod,&ddslen);
  376. } while(!bodfound);
  377. if(!bodfound) {/* No BOD; pretend */
  378. bod = tree->data.bod;
  379. ddslen = tree->data.datasize;
  380. }
  381. tree->data.bod = bod;
  382. tree->data.ddslen = ddslen;
  383. /* copy out the dds */
  384. if(ddslen > 0) {
  385. tree->text = (char*)ocmalloc(ddslen+1);
  386. memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
  387. tree->text[ddslen] = '\0';
  388. } else
  389. tree->text = NULL;
  390. /* reset the position of the tmp file*/
  391. fseek(tree->data.file,tree->data.bod,SEEK_SET);
  392. if(tree->text == NULL) stat = OC_EDATADDS;
  393. return OCTHROW(stat);
  394. }
  395. static OCerror
  396. createtempfile(OCstate* state, OCtree* tree)
  397. {
  398. int fd;
  399. char* name = NULL;
  400. fd = createtempfile1(TMPPATH1,&name);
  401. if(fd < 0)
  402. fd = createtempfile1(TMPPATH2,&name);
  403. if(fd < 0) {
  404. oc_log(LOGERR,"oc_open: attempt to open tmp file failed: %s",name);
  405. return errno;
  406. }
  407. #ifdef OCDEBUG
  408. oc_log(LOGNOTE,"oc_open: using tmp file: %s",name);
  409. #endif
  410. tree->data.filename = name; /* remember our tmp file name */
  411. tree->data.file = fdopen(fd,"w+");
  412. if(tree->data.file == NULL) return OC_EOPEN;
  413. /* unlink the temp file so it will automatically be reclaimed */
  414. if(ocdebug == 0) unlink(tree->data.filename);
  415. return OC_NOERR;
  416. }
  417. int
  418. createtempfile1(char* tmppath, char** tmpnamep)
  419. {
  420. int fd = 0;
  421. char* tmpname = NULL;
  422. tmpname = (char*)malloc(strlen(tmppath)+strlen("dataddsXXXXXX")+1);
  423. if(tmpname == NULL) return -1;
  424. strcpy(tmpname,tmppath);
  425. #ifdef HAVE_MKSTEMP
  426. strcat(tmpname,"dataddsXXXXXX");
  427. /* Note Potential problem: old versions of this function
  428. leave the file in mode 0666 instead of 0600 */
  429. fd = mkstemp(tmpname);
  430. #else /* !HAVE_MKSTEMP */
  431. /* Need to simulate by using some kind of pseudo-random number */
  432. strcat(tmpname,"datadds");
  433. {
  434. int rno = rand();
  435. char spid[7];
  436. if(rno < 0) rno = -rno;
  437. sprintf(spid,"%06d",rno);
  438. strcat(tmpname,spid);
  439. # ifdef WIN32
  440. fd=open(tmpname,O_RDWR|O_BINARY|O_CREAT|O_EXCL|FILE_ATTRIBUTE_TEMPORARY, _S_IREAD|_S_IWRITE);
  441. # else
  442. fd=open(tmpname,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
  443. # endif
  444. }
  445. #endif /* !HAVE_MKSTEMP */
  446. if(tmpname == NULL) return -1;
  447. if(tmpnamep) *tmpnamep = tmpname;
  448. return fd;
  449. }
  450. /* Allow these (non-alpha-numerics) to pass thru */
  451. static char okchars[] = "&/:;,.=?@'\"<>{}!|\\^[]`~";
  452. static char hexdigits[] = "0123456789abcdef";
  453. /* Modify constraint to use %XX escapes */
  454. static char*
  455. constraintescape(const char* url)
  456. {
  457. size_t len;
  458. char* p;
  459. int c;
  460. char* eurl;
  461. if(url == NULL) return NULL;
  462. len = strlen(url);
  463. eurl = ocmalloc(1+3*len); /* worst case: c -> %xx */
  464. MEMCHECK(eurl,NULL);
  465. p = eurl;
  466. *p = '\0';
  467. while((c=*url++)) {
  468. if(c >= '0' && c <= '9') {*p++ = c;}
  469. else if(c >= 'a' && c <= 'z') {*p++ = c;}
  470. else if(c >= 'A' && c <= 'Z') {*p++ = c;}
  471. else if(strchr(okchars,c) != NULL) {*p++ = c;}
  472. else {
  473. *p++ = '%';
  474. *p++ = hexdigits[(c & 0xf0)>>4];
  475. *p++ = hexdigits[(c & 0xf)];
  476. }
  477. }
  478. *p = '\0';
  479. return eurl;
  480. }
  481. OCerror
  482. ocupdatelastmodifieddata(OCstate* state)
  483. {
  484. OCerror status = OC_NOERR;
  485. long lastmodified;
  486. char* base = NULL;
  487. base = ocuribuild(state->uri,NULL,NULL,OCURIENCODE);
  488. status = ocfetchlastmodified(state->curl, base, &lastmodified);
  489. free(base);
  490. if(status == OC_NOERR) {
  491. state->datalastmodified = lastmodified;
  492. }
  493. return status;
  494. }
  495. /*
  496. Set curl properties for link based on rc files
  497. */
  498. static void
  499. ocsetcurlproperties(OCstate* state)
  500. {
  501. CURLcode cstat = CURLE_OK;
  502. /* process the triple store wrt to this state */
  503. if(ocdodsrc_process(state) != OC_NOERR) {
  504. oc_log(LOGERR,"Malformed .opendaprc configuration file");
  505. goto fail;
  506. }
  507. if(state->creds.username == NULL && state->creds.password == NULL) {
  508. if(state->uri->user != NULL && state->uri->password != NULL) {
  509. /* this overrides .dodsrc */
  510. if(state->creds.password) free(state->creds.password);
  511. state->creds.password = nulldup(state->uri->password);
  512. if(state->creds.username) free(state->creds.username);
  513. state->creds.username = nulldup(state->uri->user);
  514. }
  515. }
  516. return;
  517. fail:
  518. if(cstat != CURLE_OK)
  519. oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
  520. return;
  521. }