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