Line data Source code
1 : #include "burp.h"
2 : #include "alloc.h"
3 : #include "asfd.h"
4 : #include "async.h"
5 : #include "berrno.h"
6 : #include "cmd.h"
7 : #include "fsops.h"
8 : #include "fzp.h"
9 : #include "handy.h"
10 : #include "handy_extra.h"
11 : #include "hexmap.h"
12 : #include "iobuf.h"
13 : #include "log.h"
14 : #include "msg.h"
15 : #include "prepend.h"
16 :
17 : #include <sys/types.h>
18 : #include <sys/socket.h>
19 :
20 : #ifdef HAVE_WIN32
21 : #include <winsock2.h>
22 : #include <ws2tcpip.h>
23 : #endif
24 :
25 : // return -1 for error, 0 for OK, 1 if the client wants to interrupt the
26 : // transfer.
27 6 : int do_quick_read(struct asfd *asfd, const char *datapth, struct cntr *cntr)
28 : {
29 6 : int r=0;
30 : struct iobuf *rbuf;
31 6 : if(asfd->as->read_quick(asfd->as)) return -1;
32 6 : rbuf=asfd->rbuf;
33 :
34 6 : if(rbuf->buf)
35 : {
36 0 : if(rbuf->cmd==CMD_MESSAGE
37 0 : || rbuf->cmd==CMD_WARNING)
38 : {
39 0 : log_recvd(rbuf, cntr, 0);
40 : }
41 0 : else if(rbuf->cmd==CMD_INTERRUPT)
42 : {
43 : // Client wants to interrupt - double check that
44 : // it is still talking about the file that we are
45 : // sending.
46 0 : if(datapth && !strcmp(rbuf->buf, datapth))
47 0 : r=1;
48 : }
49 : else
50 : {
51 0 : iobuf_log_unexpected(rbuf, __func__);
52 0 : r=-1;
53 : }
54 0 : iobuf_free_content(rbuf);
55 : }
56 : return r;
57 : }
58 :
59 0 : static int send_whole_file_gz(struct asfd *asfd,
60 : const char *datapth, int quick_read,
61 : uint64_t *bytes, struct cntr *cntr,
62 : int compression, struct fzp *fzp)
63 : {
64 0 : int ret=0;
65 0 : int zret=0;
66 :
67 : unsigned have;
68 : z_stream strm;
69 0 : int flush=Z_NO_FLUSH;
70 : uint8_t in[ZCHUNK];
71 : uint8_t out[ZCHUNK];
72 :
73 : struct iobuf wbuf;
74 :
75 : /* allocate deflate state */
76 0 : strm.zalloc = Z_NULL;
77 0 : strm.zfree = Z_NULL;
78 0 : strm.opaque = Z_NULL;
79 0 : if((zret=deflateInit2(&strm, compression, Z_DEFLATED, (15+16),
80 : 8, Z_DEFAULT_STRATEGY))!=Z_OK)
81 : return -1;
82 :
83 : do
84 : {
85 0 : strm.avail_in=fzp_read(fzp, in, ZCHUNK);
86 0 : if(!compression && !strm.avail_in) break;
87 :
88 0 : *bytes+=strm.avail_in;
89 :
90 0 : if(strm.avail_in) flush=Z_NO_FLUSH;
91 0 : else flush=Z_FINISH;
92 :
93 0 : strm.next_in=in;
94 :
95 : // Run deflate() on input until output buffer not full, finish
96 : // compression if all of source has been read in.
97 : do
98 : {
99 0 : if(compression)
100 : {
101 0 : strm.avail_out=ZCHUNK;
102 0 : strm.next_out=out;
103 0 : zret=deflate(&strm, flush);
104 0 : if(zret==Z_STREAM_ERROR)
105 : {
106 0 : logp("z_stream_error\n");
107 0 : ret=-1;
108 0 : break;
109 : }
110 0 : have=ZCHUNK-strm.avail_out;
111 : }
112 : else
113 : {
114 0 : have=strm.avail_in;
115 0 : memcpy(out, in, have);
116 : }
117 :
118 0 : wbuf.cmd=CMD_APPEND;
119 0 : wbuf.buf=(char *)out;
120 0 : wbuf.len=have;
121 0 : if(asfd->write(asfd, &wbuf))
122 : {
123 : ret=-1;
124 : break;
125 : }
126 0 : if(quick_read && datapth)
127 : {
128 : int qr;
129 0 : if((qr=do_quick_read(asfd, datapth, cntr))<0)
130 : {
131 : ret=-1;
132 : break;
133 : }
134 0 : if(qr) // Client wants to interrupt.
135 : {
136 : goto cleanup;
137 : }
138 : }
139 0 : if(!compression) break;
140 0 : } while(!strm.avail_out);
141 :
142 0 : if(ret) break;
143 :
144 0 : if(!compression) continue;
145 :
146 0 : if(strm.avail_in) /* all input will be used */
147 : {
148 0 : ret=-1;
149 0 : logp("strm.avail_in=%d\n", strm.avail_in);
150 0 : break;
151 : }
152 0 : } while(flush!=Z_FINISH);
153 :
154 0 : if(!ret)
155 : {
156 0 : if(compression && zret!=Z_STREAM_END)
157 : {
158 0 : logp("ret OK, but zstream not finished: %d\n", zret);
159 0 : ret=-1;
160 : }
161 : }
162 :
163 : cleanup:
164 0 : deflateEnd(&strm);
165 :
166 0 : if(!ret)
167 : {
168 0 : return write_endfile(asfd, *bytes, NULL);
169 : }
170 : //logp("end of send\n");
171 : return ret;
172 : }
173 :
174 11 : int set_non_blocking(int fd)
175 : {
176 : int flags;
177 11 : if((flags = fcntl(fd, F_GETFL, 0))<0) flags = 0;
178 11 : return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
179 : }
180 :
181 1 : int set_blocking(int fd)
182 : {
183 : int flags;
184 1 : if((flags = fcntl(fd, F_GETFL, 0))<0) flags = 0;
185 1 : return fcntl(fd, F_SETFL, flags | ~O_NONBLOCK);
186 : }
187 :
188 5 : char *get_tmp_filename(const char *basis)
189 : {
190 5 : return prepend(basis, ".tmp");
191 : }
192 :
193 3 : void add_fd_to_sets(int fd, fd_set *read_set, fd_set *write_set, fd_set *err_set, int *max_fd)
194 : {
195 3 : if(read_set) FD_SET((unsigned int) fd, read_set);
196 3 : if(write_set) FD_SET((unsigned int) fd, write_set);
197 3 : if(err_set) FD_SET((unsigned int) fd, err_set);
198 :
199 3 : if(fd > *max_fd) *max_fd = fd;
200 3 : }
201 :
202 : #ifndef HAVE_WIN32
203 0 : int get_address_and_port(struct sockaddr_storage *addr,
204 : char *addrstr, size_t len, uint16_t *port)
205 : {
206 : struct sockaddr_in *s4;
207 : struct sockaddr_in6 *s6;
208 :
209 0 : switch(addr->ss_family)
210 : {
211 : case AF_INET:
212 0 : s4=(struct sockaddr_in *)addr;
213 0 : inet_ntop(AF_INET, &s4->sin_addr, addrstr, len);
214 0 : *port=ntohs(s4->sin_port);
215 0 : break;
216 : case AF_INET6:
217 0 : s6=(struct sockaddr_in6 *)addr;
218 0 : inet_ntop(AF_INET6, &s6->sin6_addr, addrstr, len);
219 0 : *port=ntohs(s6->sin6_port);
220 0 : break;
221 : default:
222 0 : logp("unknown addr.ss_family: %d\n", addr->ss_family);
223 0 : return -1;
224 : }
225 : return 0;
226 : }
227 : #endif
228 :
229 0 : int set_peer_env_vars(struct sockaddr_storage *addr)
230 : {
231 : #ifndef HAVE_WIN32
232 0 : uint16_t port=0;
233 0 : char portstr[16]="";
234 0 : char addrstr[INET6_ADDRSTRLEN]="";
235 :
236 0 : if(get_address_and_port(addr, addrstr, INET6_ADDRSTRLEN, &port))
237 : return -1;
238 :
239 0 : if(setenv("REMOTE_ADDR", addrstr, 1))
240 : {
241 0 : logp("setenv REMOTE_ADDR to %s failed: %s\n",
242 0 : addrstr, strerror(errno));
243 0 : return -1;
244 : }
245 0 : snprintf(portstr, sizeof(portstr), "%d", port);
246 0 : if(setenv("REMOTE_PORT", portstr, 1))
247 : {
248 0 : logp("setenv REMOTE_PORT failed: %s\n", strerror(errno));
249 0 : return -1;
250 : }
251 : #endif
252 : return 0;
253 : }
254 :
255 0 : int set_keepalive(int fd, int value)
256 : {
257 0 : int keepalive=value;
258 0 : if(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
259 : (char *)&keepalive, sizeof(keepalive)))
260 : {
261 0 : logp("setsockopt keepalive=%d failed: %s\n",
262 0 : value, strerror(errno));
263 0 : return -1;
264 : }
265 : return 0;
266 : }
267 :
268 0 : int init_client_socket(const char *host, const char *port)
269 : {
270 0 : int rfd=-1;
271 : int gai_ret;
272 : struct addrinfo hints;
273 : struct addrinfo *result;
274 : struct addrinfo *rp;
275 :
276 0 : memset(&hints, 0, sizeof(struct addrinfo));
277 : hints.ai_family = AF_UNSPEC;
278 0 : hints.ai_socktype = SOCK_STREAM;
279 : hints.ai_flags = 0;
280 : hints.ai_protocol = 0;
281 :
282 0 : logp("Connecting to %s:%s\n", host?host:"loopback", port);
283 :
284 0 : if((gai_ret=getaddrinfo(host, port, &hints, &result)))
285 : {
286 0 : logp("getaddrinfo: %s\n", gai_strerror(gai_ret));
287 0 : return -1;
288 : }
289 :
290 0 : for(rp=result; rp; rp=rp->ai_next)
291 : {
292 0 : rfd=socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
293 0 : if(rfd<0) continue;
294 0 : set_keepalive(rfd, 1);
295 0 : if(connect(rfd, rp->ai_addr, rp->ai_addrlen) != -1) break;
296 0 : close_fd(&rfd);
297 : }
298 0 : freeaddrinfo(result);
299 0 : if(!rp)
300 : {
301 : // host==NULL and AI_PASSIVE not set -> loopback
302 0 : logp("Could not connect to %s:%s\n",
303 : host?host:"loopback", port);
304 0 : close_fd(&rfd);
305 0 : return -1;
306 : }
307 0 : reuseaddr(rfd);
308 :
309 : #ifdef HAVE_WIN32
310 : setmode(rfd, O_BINARY);
311 : #endif
312 0 : return rfd;
313 : }
314 :
315 0 : void reuseaddr(int fd)
316 : {
317 0 : int optval=1;
318 : #ifdef HAVE_OLD_SOCKOPT
319 : #define sockopt_val_t char *
320 : #else
321 : #define sockopt_val_t void *
322 : #endif
323 0 : if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
324 : (sockopt_val_t)&optval, sizeof(optval))<0)
325 0 : logp("Error: setsockopt SO_REUSEADDR: %s",
326 0 : strerror(errno));
327 0 : }
328 :
329 2 : void setup_signal(int sig, void handler(int sig))
330 : {
331 : struct sigaction sa;
332 2 : memset(&sa, 0, sizeof(sa));
333 2 : sa.sa_handler=handler;
334 2 : sigaction(sig, &sa, NULL);
335 2 : }
336 :
337 : /* Function based on src/lib/priv.c from bacula. */
338 0 : int chuser_and_or_chgrp(const char *user, const char *group, int readall)
339 : {
340 : #ifdef HAVE_WIN32
341 : return 0;
342 : #else
343 0 : struct passwd *passw = NULL;
344 0 : struct group *grp = NULL;
345 : gid_t gid;
346 : uid_t uid;
347 0 : char *username=NULL;
348 :
349 : // Allow setting readall=1 without setting user
350 0 : if(readall && !user)
351 0 : user="nobody";
352 0 : if(!user && !group) return 0;
353 :
354 0 : if(user)
355 : {
356 0 : if(!(passw=getpwnam(user)))
357 : {
358 0 : logp("could not find user '%s': %s\n",
359 0 : user, strerror(errno));
360 0 : return -1;
361 : }
362 : }
363 : else
364 : {
365 0 : if(!(passw=getpwuid(getuid())))
366 : {
367 0 : logp("could not find password entry: %s\n",
368 0 : strerror(errno));
369 0 : return -1;
370 : }
371 0 : user=passw->pw_name;
372 : }
373 : // Any OS uname pointer may get overwritten, so save name, uid, and gid
374 0 : if(!(username=strdup_w(user, __func__)))
375 : return -1;
376 0 : uid=passw->pw_uid;
377 0 : gid=passw->pw_gid;
378 0 : if(group)
379 : {
380 0 : if(!(grp=getgrnam(group)))
381 : {
382 0 : logp("could not find group '%s': %s\n", group,
383 0 : strerror(errno));
384 0 : goto err;
385 : }
386 0 : gid=grp->gr_gid;
387 : } else {
388 : // Resolve gid to group name for logp()
389 0 : if (!(grp=getgrgid(gid)))
390 : {
391 0 : logp("could not find group for gid %d: %s\n", gid,
392 0 : strerror(errno));
393 0 : goto err;
394 : }
395 0 : group=grp->gr_name;
396 0 : grp=NULL;
397 : }
398 0 : if(gid!=getgid() // do not do it if we already have the same gid.
399 0 : && initgroups(username, gid))
400 : {
401 0 : if(grp)
402 0 : logp("could not initgroups for group '%s', user '%s': %s\n", group, username, strerror(errno));
403 : else
404 0 : logp("could not initgroups for user '%s': %s\n", username, strerror(errno));
405 : goto err;
406 : }
407 0 : if(grp)
408 : {
409 0 : if(gid!=getgid() // do not do it if we already have the same gid
410 0 : && setgid(gid))
411 : {
412 0 : logp("could not set group '%s': %s\n", group,
413 0 : strerror(errno));
414 0 : goto err;
415 : }
416 : }
417 0 : if (readall)
418 : {
419 : #ifdef ENABLE_KEEP_READALL_CAPS_SUPPORT
420 : cap_t caps;
421 : // Make capabilities pass through setreuid
422 : if(prctl(PR_SET_KEEPCAPS, 1))
423 : {
424 : logp("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
425 : goto err;
426 : }
427 : if(setreuid(uid, uid))
428 : {
429 : logp("Could not switch to user=%s (uid=%u): %s\n", username, uid, strerror(errno));
430 : goto err;
431 : }
432 : // `ep' is Effective and Permitted
433 : caps=cap_from_text("cap_dac_read_search=ep");
434 : if(!caps)
435 : {
436 : logp("cap_from_text() failed: %s\n", strerror(errno));
437 : goto err;
438 : }
439 : if(cap_set_proc(caps) < 0)
440 : {
441 : logp("cap_set_proc() failed: %s\n", strerror(errno));
442 : goto err;
443 : }
444 : cap_free(caps);
445 : logp("Privileges switched to %s keeping readall capability.\n", username);
446 : #else
447 0 : logp("Keep readall capabilities is not implemented on this platform yet\n");
448 0 : goto err;
449 : #endif
450 0 : } else if(uid!=getuid() // do not do it if we already have the same uid
451 0 : && setuid(uid))
452 : {
453 0 : logp("could not set specified user '%s': %s\n", username,
454 0 : strerror(errno));
455 0 : goto err;
456 : }
457 : return 0;
458 : err:
459 0 : free_w(&username);
460 0 : return -1;
461 : #endif
462 : }
463 :
464 : // Not in dpth.c so that Windows client can see it.
465 14 : int dpth_is_compressed(int compressed, const char *datapath)
466 : {
467 14 : const char *dp=NULL;
468 :
469 14 : if(compressed>0) return compressed;
470 12 : if(compressed==0) return 0;
471 :
472 : /* Legacy - if the compressed value is -1 - that is, it is not set in
473 : the manifest, deduce the value from the datapath. */
474 6 : if((dp=strrchr(datapath, '.')) && !strcmp(dp, ".gz")) return 1;
475 6 : return 0;
476 : }
477 :
478 211 : long version_to_long(const char *version)
479 : {
480 211 : long ret=0;
481 211 : char *copy=NULL;
482 211 : char *tok1=NULL;
483 211 : char *tok2=NULL;
484 211 : char *tok3=NULL;
485 211 : if(!version || !*version) return 0;
486 208 : if(!(copy=strdup_w(version, __func__)))
487 : return -1;
488 208 : if(!(tok1=strtok(copy, "."))
489 208 : || !(tok2=strtok(NULL, "."))
490 208 : || !(tok3=strtok(NULL, ".")))
491 : {
492 0 : free_w(©);
493 0 : return -1;
494 : }
495 208 : ret+=atol(tok3);
496 208 : ret+=atol(tok2)*100;
497 208 : ret+=atol(tok1)*100*100;
498 208 : free_w(©);
499 208 : return ret;
500 : }
501 :
502 : /* These receive_a_file() and send_a_file() functions are for use by
503 : extra_comms and the CA stuff, rather than backups/restores. */
504 0 : int receive_a_file(struct asfd *asfd, const char *path, struct cntr *cntr)
505 : {
506 0 : int ret=-1;
507 0 : struct BFILE *bfd=NULL;
508 0 : uint64_t rcvdbytes=0;
509 0 : uint64_t sentbytes=0;
510 :
511 0 : if(!(bfd=bfile_alloc())) goto end;
512 0 : bfile_init(bfd, 0, 0, cntr);
513 : #ifdef HAVE_WIN32
514 : bfd->set_win32_api(bfd, 0);
515 : #else
516 0 : bfd->set_vss_strip(bfd, 0);
517 : #endif
518 0 : if(bfd->open(bfd, asfd, path,
519 : #ifdef O_NOFOLLOW
520 : O_NOFOLLOW |
521 : #endif
522 : O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
523 : S_IRUSR | S_IWUSR))
524 : {
525 : struct berrno be;
526 0 : berrno_init(&be);
527 0 : logp("Could not open for writing %s: %s\n",
528 0 : path, berrno_bstrerror(&be, errno));
529 : goto end;
530 : }
531 :
532 0 : ret=transfer_gzfile_in(asfd, bfd, &rcvdbytes, &sentbytes);
533 0 : if(bfd->close(bfd, asfd))
534 : {
535 0 : logp("error closing %s in %s\n", path, __func__);
536 0 : goto end;
537 : }
538 0 : logp("Received: %s\n", path);
539 0 : ret=0;
540 : end:
541 0 : bfd->close(bfd, asfd);
542 0 : bfile_free(&bfd);
543 0 : return ret;
544 : }
545 :
546 : /* Windows will use this function, when sending a certificate signing request.
547 : It is not using the Windows API stuff because it needs to arrive on the
548 : server side without any junk in it. */
549 0 : int send_a_file(struct asfd *asfd, const char *path, struct cntr *cntr)
550 : {
551 0 : int ret=0;
552 0 : struct fzp *fzp=NULL;
553 0 : uint64_t bytes=0;
554 0 : if(!(fzp=fzp_open(path, "rb"))
555 0 : || send_whole_file_gz(asfd, "datapth", 0, &bytes,
556 : cntr, 9 /*compression*/, fzp))
557 : {
558 : ret=-1;
559 : goto end;
560 : }
561 0 : logp("Sent %s\n", path);
562 : end:
563 0 : fzp_close(&fzp);
564 0 : return ret;
565 : }
566 :
567 174 : int strncmp_w(const char *s1, const char *s2)
568 : {
569 174 : return strncmp(s1, s2, strlen(s2));
570 : }
571 :
572 0 : char *strreplace_w(char *orig, char *search, char *replace, const char *func)
573 : {
574 0 : char *result=NULL; // the return string
575 : char *ins; // the next insert point
576 : char *tmp; // varies
577 : int len_rep; // length of replace (the string to replace search with)
578 : int len_search; // length of search (the string to look for)
579 : int len_front; // distance between rep and end of last rep
580 : int count; // number of replacements
581 :
582 : // sanity checks and initialization
583 0 : if(!orig || !search) goto end;
584 0 : len_search = strlen(search);
585 0 : if(len_search==0)
586 : goto end;
587 0 : if(!replace)
588 : len_rep=0;
589 : else
590 0 : len_rep=strlen(replace);
591 :
592 : // count the number of replacements needed
593 0 : ins=orig;
594 0 : for(count=0; (tmp=strstr(ins, search)); ++count)
595 0 : ins=tmp+len_search;
596 :
597 0 : tmp=result=(char *)malloc_w(strlen(orig)+(len_rep-len_search)*count+1, func);
598 :
599 0 : if(!result) goto end;
600 :
601 0 : while(count--)
602 : {
603 0 : ins=strstr(orig, search);
604 0 : len_front=ins-orig;
605 0 : tmp=strncpy(tmp, orig, len_front)+len_front;
606 0 : tmp=strcpy(tmp, replace)+len_rep;
607 0 : orig+=len_front+len_search; // move to next "end of rep"
608 : }
609 0 : strcpy(tmp, orig);
610 : end:
611 0 : return result;
612 : }
613 :
614 18 : static int charcount_noescaped(const char *orig, char search, int repeat)
615 : {
616 18 : int count=0;
617 : int len;
618 : int i;
619 18 : char quote='\0';
620 18 : char prev='\0';
621 18 : if(!orig) return count;
622 18 : len=strlen(orig);
623 860 : for(count=0, i=0; i<len; i++)
624 : {
625 842 : if(quote=='\0' && (orig[i]=='\'' || orig[i]=='"'))
626 : quote=orig[i];
627 839 : else if(quote!='\0' && orig[i]==quote)
628 : {
629 : // ignore escaped quote
630 3 : if(i>0 && orig[i-1]=='\\')
631 : goto loop_tail;
632 3 : quote='\0';
633 : }
634 836 : else if(quote=='\0' && orig[i]==search)
635 : {
636 : // ignore escaped char
637 43 : if(i>0 && orig[i-1]=='\\')
638 : goto loop_tail;
639 43 : if(repeat || prev!=orig[i])
640 39 : count++;
641 : }
642 : loop_tail:
643 842 : prev=orig[i];
644 : }
645 : return count;
646 : }
647 :
648 12 : char *charreplace_noescaped_w(const char *orig, char search, const char *replace, int *count, const char *func)
649 : {
650 12 : char *result=NULL;
651 : char *tmp;
652 12 : char quote='\0';
653 12 : int nb_repl=0; // number of replacement
654 : int i;
655 : int len;
656 : int len_replace;
657 : int len_dest;
658 :
659 12 : if(!orig || !search) goto end;
660 :
661 12 : len=strlen(orig);
662 12 : len_replace=strlen(replace);
663 :
664 12 : if(!(nb_repl=charcount_noescaped(orig, search, 1)))
665 : {
666 7 : result=strdup_w(orig, func);
667 7 : goto end;
668 : }
669 :
670 5 : len_dest=len+((len_replace-1)*nb_repl)+1;
671 5 : tmp=result=(char *)malloc_w(len_dest, func);
672 5 : if(!result) goto end;
673 :
674 : quote='\0';
675 363 : for(i=0; i<len; i++)
676 : {
677 363 : if(quote=='\0' && (orig[i]=='\'' || orig[i]=='"'))
678 : quote=orig[i];
679 361 : else if(quote!='\0' && orig[i]==quote)
680 : {
681 2 : if(i<=0 || orig[i-1]!='\\')
682 2 : quote='\0';
683 : }
684 359 : else if(quote=='\0' && orig[i]==search)
685 : {
686 9 : if(i<=0 || orig[i-1]!='\\')
687 : {
688 9 : tmp=(char *)memcpy(tmp, replace, len_replace);
689 9 : tmp+=len_replace;
690 9 : continue;
691 : }
692 : }
693 354 : *tmp=orig[i];
694 354 : tmp++;
695 : }
696 5 : *tmp='\0';
697 : end:
698 12 : *count=nb_repl;
699 12 : return result;
700 : }
701 :
702 : /*
703 : * Returns NULL-terminated list of tokens found in string src,
704 : * also sets *size to number of tokens found (list length without final NULL).
705 : * On failure returns NULL. List itself and tokens are dynamically allocated.
706 : * Calls to strtok with delimiters in second argument are used (see its docs),
707 : * but neither src nor delimiters arguments are altered.
708 : */
709 0 : char **strsplit_w(const char *src, const char *delimiters, size_t *size, const char *func)
710 : {
711 : size_t allocated;
712 0 : char *init=NULL;
713 0 : char **ret=NULL;
714 :
715 0 : *size=0;
716 0 : if(!(init=strdup_w(src, func))) goto end;
717 0 : if(!(ret=(char **)malloc_w((allocated=10)*sizeof(char *), func)))
718 : goto end;
719 0 : for(char *tmp=strtok(init, delimiters); tmp; tmp=strtok(NULL, delimiters))
720 : {
721 : // Check if space is present for another token and terminating NULL.
722 0 : if(allocated<*size+2)
723 : {
724 0 : if(!(ret=(char **)realloc_w(ret,
725 0 : (allocated=*size+11)*sizeof(char *), func)))
726 : goto end;
727 : }
728 0 : if(!(ret[(*size)++]=strdup_w(tmp, func)))
729 : {
730 : ret=NULL;
731 : goto end;
732 : }
733 : }
734 0 : ret[*size]=NULL;
735 :
736 : end:
737 0 : free_w(&init);
738 0 : return ret;
739 : }
740 :
741 6 : static char *strip_whitespace_w(const char *src, const char *func)
742 : {
743 6 : char *ret=NULL;
744 6 : char *ptr=(char *)src;
745 6 : int len=strlen(src);
746 : int size;
747 6 : if(*ptr!=' ' && ptr[len-1]!=' ')
748 : {
749 3 : if(!(ret=strdup_w(src, func))) goto end;
750 : return ret;
751 : }
752 3 : for(; *ptr==' '; ptr++);
753 3 : size=strlen(ptr);
754 3 : for(; ptr[size-1]==' '; --size);
755 3 : if(!(ret=(char *)malloc_w(size+2, func))) goto end;
756 3 : ret=strncpy(ret, ptr, size);
757 3 : ret[size]='\0';
758 : end:
759 : return ret;
760 : }
761 :
762 : // same as strsplit_w except the delimiter is a single char and if the delimiter
763 : // is inside quotes or escaped with '\' it is ignored.
764 6 : char **charsplit_noescaped_w(const char *src, char delimiter, size_t *size, const char *func)
765 : {
766 6 : char **ret=NULL;
767 6 : char *ptr=NULL;
768 : char *buf;
769 : char *end;
770 6 : char quote='\0';
771 6 : char prev='\0';
772 : int count;
773 : int i, j, k;
774 : int len;
775 :
776 6 : if(!src) goto end;
777 6 : ptr=strip_whitespace_w(src, func);
778 6 : buf=ptr;
779 6 : len=strlen(ptr);
780 6 : if(!(count=charcount_noescaped(ptr, delimiter, 0)))
781 : goto end;
782 : // need one more space than the number of delimiters
783 6 : count++;
784 6 : if(!(ret=(char **)malloc_w((count+1)*sizeof(char *), func)))
785 : goto error;
786 6 : *size=(size_t)count;
787 292 : for(i=0, j=0, k=0; i<len; i++)
788 : {
789 286 : if(quote=='\0' && (ptr[i]=='\'' || ptr[i]=='"'))
790 : quote=ptr[i];
791 285 : else if(quote!='\0' && ptr[i]==quote)
792 : {
793 1 : if(i<=0 || ptr[i-1]!='\\')
794 1 : quote='\0';
795 : }
796 284 : else if(quote=='\0' && ptr[i]==delimiter)
797 : {
798 34 : if(i<=0 || ptr[i-1]!='\\')
799 : {
800 34 : if(prev==ptr[i])
801 4 : buf++;
802 : else
803 : {
804 : char *tmp;
805 30 : int tmp_len=j+1;
806 30 : if(k>0) buf++;
807 30 : if(!(tmp=(char *)malloc_w(
808 : tmp_len, func)))
809 : goto error;
810 30 : tmp=strncpy(tmp, buf, tmp_len);
811 30 : tmp[tmp_len-1]='\0';
812 30 : ret[k]=tmp;
813 30 : buf+=j;
814 30 : j=0;
815 30 : k++;
816 : }
817 : goto loop_tail;
818 : }
819 : }
820 252 : j++;
821 : loop_tail:
822 286 : prev=ptr[i];
823 : }
824 6 : while(*buf==delimiter && *(buf-1)!='\\') buf++;
825 6 : if(!(end=(char *)malloc_w(j+1, func)))
826 : goto error;
827 6 : end=strncpy(end, buf, j+1);
828 6 : end[j]='\0';
829 6 : ret[k]=end;
830 6 : ret[k+1]=NULL;
831 : end:
832 6 : free_w(&ptr);
833 6 : return ret;
834 : error:
835 0 : free_w(&ptr);
836 0 : free_list_w(&ret, *size);
837 0 : return NULL;
838 : }
839 :
840 6 : void free_list_w(char ***list, size_t size)
841 : {
842 6 : char **l=*list;
843 6 : if(!l) return;
844 : size_t i;
845 36 : for(i=0; i<size; i++)
846 36 : if(l[i]) free_w(&l[i]);
847 6 : free_v((void **)list);
848 : }
849 :
850 : // Strip any trailing slashes (unless it is '/').
851 9 : void strip_trailing_slashes(char **str)
852 : {
853 : size_t l;
854 : // FIX THIS: pretty crappy.
855 : while(1)
856 : {
857 11 : if(!str || !*str
858 10 : || !strcmp(*str, "/")
859 10 : || !(l=strlen(*str))
860 10 : || (*str)[l-1]!='/')
861 9 : return;
862 1 : (*str)[l-1]='\0';
863 : }
864 : }
865 :
866 0 : int breakpoint(int breakpoint, const char *func)
867 : {
868 0 : logp("Breakpoint %d hit in %s\n", breakpoint, func);
869 0 : return -1;
870 : }
871 :
872 : /* Windows users have a nasty habit of putting in backslashes. Convert them. */
873 : #ifdef HAVE_WIN32
874 : void convert_backslashes(char **path)
875 : {
876 : char *p=NULL;
877 : for(p=*path; *p; p++) if(*p=='\\') *p='/';
878 : }
879 : #endif
880 :
881 3 : char *strlwr(char *s)
882 : {
883 3 : char *tmp=s;
884 3 : for(;*tmp;++tmp) *tmp=tolower((unsigned char)*tmp);
885 3 : return s;
886 : }
887 :
888 1 : void strip_fqdn(char **fqdn)
889 : {
890 : char *tmp;
891 1 : if(!fqdn || !*fqdn)
892 : return;
893 1 : if((tmp=strchr(*fqdn, '.')))
894 1 : *tmp='\0';
895 : }
|