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: 2021-08-30 21:21:43 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 "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(&copy);
     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(&copy);
     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             : }

Generated by: LCOV version 1.13