LCOV - code coverage report
Current view: top level - src - handy.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 166 406 40.9 %
Date: 2022-12-03 01:09:05 Functions: 17 29 58.6 %

          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(&copy);
     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(&copy);
     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             : }

Generated by: LCOV version 1.13