1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  *********************************************************************/
5 
6 #include "config.h"
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
13 #ifdef HAVE_SYS_STAT_H
14 #include <sys/stat.h>
15 #endif
16 #ifdef HAVE_FCNTL_H
17 #include <fcntl.h>
18 #endif
19 #ifdef _MSC_VER
20 #include <io.h>
21 #endif
22 #include "netcdf.h"
23 #include "ncuri.h"
24 #include "ncbytes.h"
25 #include "nclist.h"
26 #include "nclog.h"
27 #include "ncwinpath.h"
28 
29 extern int mkstemp(char *template);
30 
31 #define NC_MAX_PATH 4096
32 
33 #define LBRACKET '['
34 #define RBRACKET ']'
35 
36 /**************************************************/
37 /**
38  * Provide a hidden interface to allow utilities
39  * to check if a given path name is really an ncdap4 url.
40  * If no, return null, else return basename of the url
41  * minus any extension.
42  */
43 
44 int
NC__testurl(const char * path,char ** basenamep)45 NC__testurl(const char* path, char** basenamep)
46 {
47     NCURI* uri;
48     int ok = NC_NOERR;
49     if(ncuriparse(path,&uri))
50 	ok = NC_EURL;
51     else {
52 	char* slash = (uri->path == NULL ? NULL : strrchr(uri->path, '/'));
53 	char* dot;
54 	if(slash == NULL) slash = (char*)path; else slash++;
55         slash = nulldup(slash);
56         if(slash == NULL)
57             dot = NULL;
58         else
59             dot = strrchr(slash, '.');
60         if(dot != NULL &&  dot != slash) *dot = '\0';
61         if(basenamep)
62             *basenamep=slash;
63         else if(slash)
64             free(slash);
65     }
66     ncurifree(uri);
67     return ok;
68 }
69 
70 /* Return 1 if this machine is little endian */
71 int
NC_isLittleEndian(void)72 NC_isLittleEndian(void)
73 {
74     union {
75         unsigned char bytes[SIZEOF_INT];
76 	int i;
77     } u;
78     u.i = 1;
79     return (u.bytes[0] == 1 ? 1 : 0);
80 }
81 
82 char*
NC_backslashEscape(const char * s)83 NC_backslashEscape(const char* s)
84 {
85     const char* p;
86     char* q;
87     size_t len;
88     char* escaped = NULL;
89 
90     len = strlen(s);
91     escaped = (char*)malloc(1+(2*len)); /* max is everychar is escaped */
92     if(escaped == NULL) return NULL;
93     for(p=s,q=escaped;*p;p++) {
94         char c = *p;
95         switch (c) {
96 	case '\\':
97 	case '/':
98 	case '.':
99 	case '@':
100 	    *q++ = '\\'; *q++ = '\\';
101 	    break;
102 	default: *q++ = c; break;
103         }
104     }
105     *q = '\0';
106     return escaped;
107 }
108 
109 char*
NC_backslashUnescape(const char * esc)110 NC_backslashUnescape(const char* esc)
111 {
112     size_t len;
113     char* s;
114     const char* p;
115     char* q;
116 
117     if(esc == NULL) return NULL;
118     len = strlen(esc);
119     s = (char*)malloc(len+1);
120     if(s == NULL) return NULL;
121     for(p=esc,q=s;*p;) {
122 	switch (*p) {
123 	case '\\':
124 	     p++;
125 	     /* fall thru */
126 	default: *q++ = *p++; break;
127 	}
128     }
129     *q = '\0';
130     return s;
131 }
132 
133 char*
NC_entityescape(const char * s)134 NC_entityescape(const char* s)
135 {
136     const char* p;
137     char* q;
138     size_t len;
139     char* escaped = NULL;
140     const char* entity;
141 
142     len = strlen(s);
143     escaped = (char*)malloc(1+(6*len)); /* 6 = |&apos;| */
144     if(escaped == NULL) return NULL;
145     for(p=s,q=escaped;*p;p++) {
146 	char c = *p;
147 	switch (c) {
148 	case '&':  entity = "&amp;"; break;
149 	case '<':  entity = "&lt;"; break;
150 	case '>':  entity = "&gt;"; break;
151 	case '"':  entity = "&quot;"; break;
152 	case '\'': entity = "&apos;"; break;
153 	default	 : entity = NULL; break;
154 	}
155 	if(entity == NULL)
156 	    *q++ = c;
157 	else {
158 	    len = strlen(entity);
159 	    memcpy(q,entity,len);
160 	    q+=len;
161 	}
162     }
163     *q = '\0';
164     return escaped;
165 }
166 
167 /**
168 Wrap mktmp and return the generated path,
169 or null if failed.
170 Base is the base file path. XXXXX is appended
171 to allow mktmp add its unique id.
172 Return the generated path.
173 */
174 
175 char*
NC_mktmp(const char * base)176 NC_mktmp(const char* base)
177 {
178     int fd;
179     char* cvtpath = NULL;
180     char tmp[NC_MAX_PATH];
181 #ifdef HAVE_MKSTEMP
182     mode_t mask;
183 #endif
184 
185     /* Make sure that this path conversion has been applied */
186     cvtpath = NCpathcvt(base);
187     strncpy(tmp,cvtpath,sizeof(tmp));
188     nullfree(cvtpath);
189 	strncat(tmp, "XXXXXX", sizeof(tmp) - strlen(tmp) - 1);
190 
191 #ifdef HAVE_MKSTEMP
192     /* Note Potential problem: old versions of this function
193        leave the file in mode 0666 instead of 0600 */
194     mask=umask(0077);
195     fd = mkstemp(tmp);
196     (void)umask(mask);
197 #else /* !HAVE_MKSTEMP */
198     {
199 #ifdef HAVE_MKTEMP
200 #ifdef _MSC_VER
201         /* Use _mktemp_s */
202 	_mktemp_s(tmp,sizeof(tmp)-1);
203 #else /*!_MSC_VER*/
204         mktemp(tmp);
205 	tmo[sizeof[tmp]-1] = '\0';
206 #endif
207 #else /* !HAVE_MKTEMP */
208 	/* Need to simulate by using some kind of pseudo-random number */
209 	{
210 	    int rno = rand();
211 	    char spid[7];
212 	    if(rno < 0) rno = -rno;
213             snprintf(spid,sizeof(spid),"%06d",rno);
214             strncat(tmp,spid,sizeof(tmp) - strlen(tmp) - 1);
215 	}
216 #endif /* HAVE_MKTEMP */
217 #ifdef _MSC_VER
218         fd=NCopen3(tmp,O_RDWR|O_BINARY|O_CREAT, _S_IREAD|_S_IWRITE);
219 #else
220         fd=NCopen3(tmp,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
221 #endif
222     }
223 #endif /* !HAVE_MKSTEMP */
224     if(fd < 0) {
225        nclog(NCLOGERR, "Could not create temp file: %s",tmp);
226        return NULL;
227     } else
228 	close(fd);
229     return strdup(tmp);
230 }
231 
232 int
NC_readfile(const char * filename,NCbytes * content)233 NC_readfile(const char* filename, NCbytes* content)
234 {
235     int ret = NC_NOERR;
236     FILE* stream = NULL;
237     char part[1024];
238 
239 #ifdef _WIN32
240     stream = NCfopen(filename,"rb");
241 #else
242     stream = NCfopen(filename,"r");
243 #endif
244     if(stream == NULL) {ret=errno; goto done;}
245     for(;;) {
246 	size_t count = fread(part, 1, sizeof(part), stream);
247 	if(count <= 0) break;
248 	ncbytesappendn(content,part,count);
249 	if(ferror(stream)) {ret = NC_EIO; goto done;}
250 	if(feof(stream)) break;
251     }
252     ncbytesnull(content);
253 done:
254     if(stream) fclose(stream);
255     return ret;
256 }
257 
258 int
NC_writefile(const char * filename,size_t size,void * content)259 NC_writefile(const char* filename, size_t size, void* content)
260 {
261     int ret = NC_NOERR;
262     FILE* stream = NULL;
263     void* p;
264     size_t remain;
265 
266 #ifdef _WIN32
267     stream = NCfopen(filename,"wb");
268 #else
269     stream = NCfopen(filename,"w");
270 #endif
271     if(stream == NULL) {ret=errno; goto done;}
272     p = content;
273     remain = size;
274     while(remain > 0) {
275 	size_t written = fwrite(p, 1, remain, stream);
276 	if(ferror(stream)) {ret = NC_EIO; goto done;}
277 	if(feof(stream)) break;
278 	remain -= written;
279     }
280 done:
281     if(stream) fclose(stream);
282     return ret;
283 }
284 
285 /*
286 Parse a path as a url and extract the modelist.
287 If the path is not a URL, then return a NULL list.
288 If a URL, but modelist is empty or does not exist,
289 then return empty list.
290 */
291 int
NC_getmodelist(const char * path,NClist ** modelistp)292 NC_getmodelist(const char* path, NClist** modelistp)
293 {
294     int stat=NC_NOERR;
295     NClist* modelist = NULL;
296     NCURI* uri = NULL;
297     const char* modestr = NULL;
298     const char* p = NULL;
299     const char* endp = NULL;
300 
301     ncuriparse(path,&uri);
302     if(uri == NULL) goto done; /* not a uri */
303 
304     /* Get the mode= arg from the fragment */
305     modelist = nclistnew();
306     modestr = ncurilookup(uri,"mode");
307     if(modestr == NULL || strlen(modestr) == 0) goto done;
308     /* Parse the mode string at the commas or EOL */
309     p = modestr;
310     for(;;) {
311 	char* s;
312 	ptrdiff_t slen;
313 	endp = strchr(p,',');
314 	if(endp == NULL) endp = p + strlen(p);
315 	slen = (endp - p);
316 	if((s = malloc(slen+1)) == NULL) {stat = NC_ENOMEM; goto done;}
317 	memcpy(s,p,slen);
318 	s[slen] = '\0';
319 	nclistpush(modelist,s);
320 	if(*endp == '\0') break;
321 	p = endp+1;
322     }
323 
324 done:
325     if(stat == NC_NOERR) {
326 	if(modelistp) {*modelistp = modelist; modelist = NULL;}
327     }
328     ncurifree(uri);
329     nclistfree(modelist);
330     return stat;
331 }
332 
333 /*
334 Check "mode=" list for a path and return 1 if present, 0 otherwise.
335 */
336 int
NC_testmode(const char * path,const char * tag)337 NC_testmode(const char* path, const char* tag)
338 {
339     int stat = NC_NOERR;
340     int found = 0;
341     int i;
342     NClist* modelist = NULL;
343 
344     if((stat = NC_getmodelist(path, &modelist))) goto done;
345     for(i=0;i<nclistlength(modelist);i++) {
346 	const char* value = nclistget(modelist,i);
347 	if(strcasecmp(tag,value)==0) {found = 1; break;}
348     }
349 
350 done:
351     nclistfreeall(modelist);
352     return found;
353 }
354