Imported Upstream version 3.2.0
[debian/amanda] / common-src / util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: util.c,v 1.42 2006/08/24 01:57:15 paddy_s Exp $
28  */
29
30 #include "amanda.h"
31 #include "util.h"
32 #include "match.h"
33 #include <regex.h>
34 #include "arglist.h"
35 #include "clock.h"
36 #include "sockaddr-util.h"
37 #include "conffile.h"
38 #include "base64.h"
39 #include "stream.h"
40 #include "pipespawn.h"
41 #include <glib.h>
42 #include <string.h>
43
44 static int make_socket(sa_family_t family);
45 static int connect_port(sockaddr_union *addrp, in_port_t port, char *proto,
46                         sockaddr_union *svaddr, int nonblock);
47
48 static int
49 make_socket(
50     sa_family_t family)
51 {
52     int s;
53     int save_errno;
54 #if defined(SO_KEEPALIVE) || defined(USE_REUSEADDR)
55     int on=1;
56     int r;
57 #endif
58
59     g_debug("make_socket opening socket with family %d", family);
60     s = socket(family, SOCK_STREAM, 0);
61     if (s == -1) {
62         save_errno = errno;
63         dbprintf(_("make_socket: socket() failed: %s\n"), strerror(save_errno));
64         errno = save_errno;
65         return -1;
66     }
67     if (s < 0 || s >= (int)FD_SETSIZE) {
68         aclose(s);
69         errno = EMFILE;                         /* out of range */
70         return -1;
71     }
72
73 #ifdef USE_REUSEADDR
74     r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
75     if (r < 0) {
76         save_errno = errno;
77         dbprintf(_("make_socket: setsockopt(SO_REUSEADDR) failed: %s\n"),
78                   strerror(errno));
79         errno = save_errno;
80     }
81 #endif
82
83 #ifdef SO_KEEPALIVE
84     r = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
85                    (void *)&on, SIZEOF(on));
86     if (r == -1) {
87         save_errno = errno;
88         dbprintf(_("make_socket: setsockopt() failed: %s\n"),
89                   strerror(save_errno));
90         aclose(s);
91         errno = save_errno;
92         return -1;
93     }
94 #endif
95
96     return s;
97 }
98
99 GQuark am_util_error_quark(void)
100 {
101     return g_quark_from_static_string("am-util-error-quark");
102 }
103
104 /* addrp is my address */
105 /* svaddr is the address of the remote machine */
106 /* return socket on success */
107 /* return -1     on failure */
108 int
109 connect_portrange(
110     sockaddr_union *addrp,
111     in_port_t           first_port,
112     in_port_t           last_port,
113     char *              proto,
114     sockaddr_union *svaddr,
115     int                 nonblock)
116 {
117     int                 s;
118     in_port_t           port;
119     static in_port_t    port_in_use[1024];
120     static int          nb_port_in_use = 0;
121     int                 i;
122     int                 save_errno = EAGAIN;
123
124     assert(first_port <= last_port);
125     /* Try a port already used */
126     for(i=0; i < nb_port_in_use; i++) {
127         port = port_in_use[i];
128         if(port >= first_port && port <= last_port) {
129             s = connect_port(addrp, port, proto, svaddr, nonblock);
130             if(s == -2) return -1;
131             if(s > 0) {
132                 return s;
133             }
134             if (errno != EAGAIN && errno != EBUSY)
135                 save_errno = errno;
136         }
137     }
138
139     /* Try a port in the range */
140     for (port = first_port; port <= last_port; port++) {
141         s = connect_port(addrp, port, proto, svaddr, nonblock);
142         if(s == -2) return -1;
143         if(s > 0) {
144             port_in_use[nb_port_in_use++] = port;
145             return s;
146         }
147         if (errno != EAGAIN && errno != EBUSY)
148             save_errno = errno;
149     }
150
151     dbprintf(_("connect_portrange: All ports between %d and %d are busy.\n"),
152               first_port,
153               last_port);
154     errno = save_errno;
155     return -1;
156 }
157
158 /* addrp is my address */
159 /* svaddr is the address of the remote machine */
160 /* return -2: Don't try again */
161 /* return -1: Try with another port */
162 /* return >0: this is the connected socket */
163 int
164 connect_port(
165     sockaddr_union *addrp,
166     in_port_t           port,
167     char *              proto,
168     sockaddr_union *svaddr,
169     int                 nonblock)
170 {
171     int                 save_errno;
172     struct servent *    servPort;
173     socklen_t_equiv     len;
174     socklen_t_equiv     socklen;
175     int                 s;
176
177     servPort = getservbyport((int)htons(port), proto);
178     if (servPort != NULL && !strstr(servPort->s_name, "amanda")) {
179         dbprintf(_("connect_port: Skip port %d: owned by %s.\n"),
180                   port, servPort->s_name);
181         errno = EBUSY;
182         return -1;
183     }
184
185     if ((s = make_socket(SU_GET_FAMILY(addrp))) == -1) return -2;
186
187     SU_SET_PORT(addrp, port);
188     socklen = SS_LEN(addrp);
189     if (bind(s, (struct sockaddr *)addrp, socklen) != 0) {
190         save_errno = errno;
191         aclose(s);
192         if(servPort == NULL) {
193             dbprintf(_("connect_port: Try  port %d: available - %s\n"),
194                      port, strerror(errno));
195         } else {
196             dbprintf(_("connect_port: Try  port %d: owned by %s - %s\n"),
197                      port, servPort->s_name, strerror(errno));
198         }
199         if (save_errno != EADDRINUSE) {
200             errno = save_errno;
201             return -2;
202         }
203
204         errno = save_errno;
205         return -1;
206     }
207     if(servPort == NULL) {
208         dbprintf(_("connect_port: Try  port %d: available - Success\n"), port);
209     } else {
210         dbprintf(_("connect_port: Try  port %d: owned by %s - Success\n"),
211                   port, servPort->s_name);
212     }
213
214     /* find out what port was actually used */
215
216     len = sizeof(*addrp);
217     if (getsockname(s, (struct sockaddr *)addrp, &len) == -1) {
218         save_errno = errno;
219         dbprintf(_("connect_port: getsockname() failed: %s\n"),
220                   strerror(save_errno));
221         aclose(s);
222         errno = save_errno;
223         return -1;
224     }
225
226     if (nonblock)
227         fcntl(s, F_SETFL, fcntl(s, F_GETFL, 0)|O_NONBLOCK);
228     if (connect(s, (struct sockaddr *)svaddr, SS_LEN(svaddr)) == -1 && !nonblock) {
229         save_errno = errno;
230         dbprintf(_("connect_portrange: Connect from %s failed: %s\n"),
231                   str_sockaddr(addrp),
232                   strerror(save_errno));
233         dbprintf(_("connect_portrange: connect to %s failed: %s\n"),
234                   str_sockaddr(svaddr),
235                   strerror(save_errno));
236         aclose(s);
237         errno = save_errno;
238         if (save_errno == ECONNREFUSED ||
239             save_errno == EHOSTUNREACH ||
240             save_errno == ENETUNREACH ||
241             save_errno == ETIMEDOUT)  {
242             return -2   ;
243         }
244         return -1;
245     }
246
247     dbprintf(_("connected to %s\n"),
248               str_sockaddr(svaddr));
249     dbprintf(_("our side is %s\n"),
250               str_sockaddr(addrp));
251     return s;
252 }
253
254
255 /*
256  * Bind to a port in the given range.  Takes a begin,end pair of port numbers.
257  *
258  * Returns negative on error (EGAIN if all ports are in use).  Returns 0
259  * on success.
260  */
261 int
262 bind_portrange(
263     int                 s,
264     sockaddr_union *addrp,
265     in_port_t           first_port,
266     in_port_t           last_port,
267     char *              proto)
268 {
269     in_port_t port;
270     in_port_t cnt;
271     socklen_t_equiv socklen;
272     struct servent *servPort;
273     const in_port_t num_ports = (in_port_t)(last_port - first_port + 1);
274     int save_errno = EAGAIN;
275
276     assert(first_port <= last_port);
277
278     /*
279      * We pick a different starting port based on our pid and the current
280      * time to avoid always picking the same reserved port twice.
281      */
282     port = (in_port_t)(((getpid() + time(0)) % num_ports) + first_port);
283
284     /*
285      * Scan through the range, trying all available ports that are either 
286      * not taken in /etc/services or registered for *amanda*.  Wrap around
287      * if we don't happen to start at the beginning.
288      */
289     for (cnt = 0; cnt < num_ports; cnt++) {
290         servPort = getservbyport((int)htons(port), proto);
291         if ((servPort == NULL) || strstr(servPort->s_name, "amanda")) {
292             SU_SET_PORT(addrp, port);
293             socklen = SS_LEN(addrp);
294             if (bind(s, (struct sockaddr *)addrp, socklen) >= 0) {
295                 if (servPort == NULL) {
296                     g_debug(_("bind_portrange2: Try  port %d: Available - Success"), port);
297                 } else {
298                     g_debug(_("bind_portrange2: Try  port %d: Owned by %s - Success."), port, servPort->s_name);
299                 }
300                 return 0;
301             }
302             if (errno != EAGAIN && errno != EBUSY)
303                 save_errno = errno;
304             if (servPort == NULL) {
305                 g_debug(_("bind_portrange2: Try  port %d: Available - %s"),
306                         port, strerror(errno));
307             } else {
308                 g_debug(_("bind_portrange2: Try  port %d: Owned by %s - %s"),
309                         port, servPort->s_name, strerror(errno));
310             }
311         } else {
312                 g_debug(_("bind_portrange2: Skip port %d: Owned by %s."),
313                       port, servPort->s_name);
314         }
315         if (++port > last_port)
316             port = first_port;
317     }
318     g_debug(_("bind_portrange: all ports between %d and %d busy"),
319                   first_port,
320                   last_port);
321     errno = save_errno;
322     return -1;
323 }
324
325 int
326 interruptible_accept(
327     int sock,
328     struct sockaddr *addr,
329     socklen_t *addrlen,
330     gboolean (*prolong)(gpointer data),
331     gpointer prolong_data)
332 {
333     SELECT_ARG_TYPE readset;
334     struct timeval tv;
335     int nfound;
336
337     if (sock < 0 || sock >= FD_SETSIZE) {
338         g_debug("interruptible_accept: bad socket %d", sock);
339         return EBADF;
340     }
341
342     memset(&readset, 0, SIZEOF(readset));
343
344     while (1) {
345         if (!prolong(prolong_data)) {
346             errno = 0;
347             return -1;
348         }
349
350         FD_ZERO(&readset);
351         FD_SET(sock, &readset);
352
353         /* try accepting for 1s */
354         memset(&tv, 0, SIZEOF(tv));
355         tv.tv_sec = 1;
356
357         nfound = select(sock+1, &readset, NULL, NULL, &tv);
358         if (nfound < 0) {
359             return -1;
360         } else if (nfound == 0) {
361             continue;
362         } else if (!FD_ISSET(sock, &readset)) {
363             g_debug("interruptible_accept: select malfunction");
364             errno = EBADF;
365             return -1;
366         } else {
367             int rv = accept(sock, addr, addrlen);
368             if (rv < 0 && errno == EAGAIN)
369                 continue;
370             return rv;
371         }
372     }
373 }
374
375 /*
376  * Writes out the entire iovec
377  */
378 ssize_t
379 full_writev(
380     int                 fd,
381     struct iovec *      iov,
382     int                 iovcnt)
383 {
384     ssize_t delta, n, total;
385
386     assert(iov != NULL);
387
388     total = 0;
389     while (iovcnt > 0) {
390         /*
391          * Write the iovec
392          */
393         n = writev(fd, iov, iovcnt);
394         if (n < 0) {
395             if (errno != EINTR)
396                 return (-1);
397         }
398         else if (n == 0) {
399             errno = EIO;
400             return (-1);
401         } else {
402             total += n;
403             /*
404              * Iterate through each iov.  Figure out what we still need
405              * to write out.
406              */
407             for (; n > 0; iovcnt--, iov++) {
408                 /* 'delta' is the bytes written from this iovec */
409                 delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len;
410                 /* subtract from the total num bytes written */
411                 n -= delta;
412                 assert(n >= 0);
413                 /* subtract from this iovec */
414                 iov->iov_len -= delta;
415                 iov->iov_base = (char *)iov->iov_base + delta;
416                 /* if this iovec isn't empty, run the writev again */
417                 if (iov->iov_len > 0)
418                     break;
419             }
420         }
421     }
422     return (total);
423 }
424
425
426 /*
427  * For backward compatibility we are trying for minimal quoting.  Unless ALWAYS
428  * is true, we only quote a string if it contains whitespace or is misquoted...
429  */
430
431 char *
432 quote_string_maybe(
433     const char *str,
434     gboolean always)
435 {
436     char *  s;
437     char *  ret;
438
439     if ((str == NULL) || (*str == '\0')) {
440         ret = stralloc("\"\"");
441     } else if (!always && (match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
442         /*
443          * String does not need to be quoted since it contains
444          * neither whitespace, control or quote characters.
445          */
446         ret = stralloc(str);
447     } else {
448         /*
449          * Allocate maximum possible string length.
450          * (a string of all quotes plus room for leading ", trailing " and NULL)
451          */
452         ret = s = alloc((strlen(str) * 2) + 2 + 1);
453         *(s++) = '"';
454         while (*str != '\0') {
455             if (*str == '\t') {
456                 *(s++) = '\\';
457                 *(s++) = 't';
458                 str++;
459                 continue;
460             } else if (*str == '\n') {
461                 *(s++) = '\\';
462                 *(s++) = 'n';
463                 str++;
464                 continue;
465             } else if (*str == '\r') {
466                 *(s++) = '\\';
467                 *(s++) = 'r';
468                 str++;
469                 continue;
470             } else if (*str == '\f') {
471                 *(s++) = '\\';
472                 *(s++) = 'f';
473                 str++;
474                 continue;
475             } else if (*str == '\\') {
476                 *(s++) = '\\';
477                 *(s++) = '\\';
478                 str++;
479                 continue;
480             }
481             if (*str == '"')
482                 *(s++) = '\\';
483             *(s++) = *(str++);
484         }
485         *(s++) = '"';
486         *s = '\0';
487     }
488     return (ret);
489 }
490
491
492 char *
493 unquote_string(
494     const char *str)
495 {
496     char * ret;
497
498     if ((str == NULL) || (*str == '\0')) {
499         ret = stralloc("");
500     } else {
501         char * in;
502         char * out;
503
504         ret = in = out = stralloc(str);
505         while (*in != '\0') {
506             if (*in == '"') {
507                 in++;
508                 continue;
509             }
510
511             if (*in == '\\') {
512                 in++;
513                 if (*in == 'n') {
514                     in++;
515                     *(out++) = '\n';
516                     continue;
517                 } else if (*in == 't') {
518                     in++;
519                     *(out++) = '\t';
520                     continue;
521                 } else if (*in == 'r') {
522                     in++;
523                     *(out++) = '\r';
524                     continue;
525                 } else if (*in == 'f') {
526                     in++;
527                     *(out++) = '\f';
528                     continue;
529                 } else if (*in >= '0' && *in <= '7') {
530                     char c = 0;
531                     int i = 0;
532
533                     while (i < 3 && *in >= '0' && *in <= '7') {
534                         c = (c << 3) + *(in++) - '0';
535                         i++;
536                     }
537                     if (c)
538                         *(out++) = c;
539                 } else if (*in == '\0') {
540                     /* trailing backslash -- ignore */
541                     break;
542                 }
543             }
544             *(out++) = *(in++);
545         }
546         *out = '\0';
547     }
548     return (ret);
549 }
550
551 gchar **
552 split_quoted_strings(
553     const gchar *string)
554 {
555     char *local;
556     char *start;
557     char *p;
558     char **result;
559     GPtrArray *strs;
560     int iq = 0;
561
562     if (!string)
563         return NULL;
564
565     p = start = local = g_strdup(string);
566     strs = g_ptr_array_new();
567
568     while (*p) {
569         if (!iq && *p == ' ') {
570             *p = '\0';
571             g_ptr_array_add(strs, unquote_string(start));
572             start = p+1;
573         } else if (*p == '\\') {
574             /* next character is taken literally; if it's a multicharacter
575              * escape (e.g., \171), that doesn't bother us here */
576             p++;
577             if (!*p) break;
578         } else if (*p == '\"') {
579             iq = ! iq;
580         }
581
582         p++;
583     }
584     if (start != string)
585         g_ptr_array_add(strs, unquote_string(start));
586
587     /* now convert strs into a strv, by stealing its references to the underlying
588      * strings */
589     result = g_new0(char *, strs->len + 1);
590     memmove(result, strs->pdata, sizeof(char *) * strs->len);
591
592     g_ptr_array_free(strs, TRUE); /* TRUE => free pdata, strings are not freed */
593     g_free(local);
594
595     return result;
596 }
597
598 char *
599 strquotedstr(char **saveptr)
600 {
601     char *  tok = strtok_r(NULL, " ", saveptr);
602     size_t      len;
603     int         in_quote;
604     int         in_backslash;
605     char       *p, *t;
606
607     if (!tok)
608         return tok;
609     len = strlen(tok);
610     in_quote = 0;
611     in_backslash = 0;
612     p = tok;
613     while (in_quote || in_backslash || *p != '\0') {
614         if (*p == '\0') {
615             /* append a new token */
616             t = strtok_r(NULL, " ", saveptr);
617             if (!t)
618                 return NULL;
619             tok[len] = ' ';
620             len = strlen(tok);
621         }
622         if (!in_backslash) {
623             if (*p == '"')
624                 in_quote = !in_quote;
625             else if (*p == '\\') {
626                 in_backslash = 1;
627             }
628         } else {
629            in_backslash = 0;
630         }
631         p++;
632     }
633     return tok;
634 }
635
636 char *
637 sanitize_string(
638     const char *str)
639 {
640     char * s;
641     char * ret;
642
643     if ((str == NULL) || (*str == '\0')) {
644         ret = stralloc("");
645     } else {
646         ret = stralloc(str);
647         for (s = ret; *s != '\0'; s++) {
648             if (iscntrl((int)*s))
649                 *s = '?';
650         }
651     }
652     return (ret);
653 }
654
655 char *hexencode_string(const char *str)
656 {
657     size_t orig_len, new_len, i;
658     GString *s;
659     gchar *ret;
660     if (!str) {
661         s = g_string_sized_new(0);
662         goto cleanup;
663     }
664     new_len = orig_len = strlen(str);
665     for (i = 0; i < orig_len; i++) {
666         if (!g_ascii_isalnum(str[i])) {
667             new_len += 2;
668         }
669     }
670     s = g_string_sized_new(new_len);
671
672     for (i = 0; i < orig_len; i++) {
673         if (g_ascii_isalnum(str[i])) {
674             g_string_append_c(s, str[i]);
675         } else {
676             g_string_append_printf(s, "%%%02hhx", str[i]);
677         }
678     }
679
680 cleanup:
681     ret = s->str;
682     g_string_free(s, FALSE);
683     return ret;
684 }
685
686 char *hexdecode_string(const char *str, GError **err)
687 {
688     size_t orig_len, new_len, i;
689     GString *s;
690     gchar *ret;
691     if (!str) {
692         s = g_string_sized_new(0);
693         goto cleanup;
694     }
695     new_len = orig_len = strlen(str);
696     for (i = 0; i < orig_len; i++) {
697         if (str[i] == '%') {
698             new_len -= 2;
699         }
700     }
701     s = g_string_sized_new(new_len);
702
703     for (i = 0; (orig_len > 2) && (i < orig_len-2); i++) {
704         if (str[i] == '%') {
705             gchar tmp = 0;
706             size_t j;
707             for (j = 1; j < 3; j++) {
708                 tmp <<= 4;
709                 if (str[i+j] >= '0' && str[i+j] <= '9') {
710                     tmp += str[i+j] - '0';
711                 } else if (str[i+j] >= 'a' && str[i+j] <= 'f') {
712                     tmp += str[i+j] - 'a' + 10;
713                 } else if (str[i+j] >= 'A' && str[i+j] <= 'F') {
714                     tmp += str[i+j] - 'A' + 10;
715                 } else {
716                     /* error */
717                     g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
718                         "Illegal character (non-hex) 0x%02hhx at offset %zd", str[i+j], i+j);
719                     g_string_truncate(s, 0);
720                     goto cleanup;
721                 }
722             }
723             if (!tmp) {
724                 g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
725                     "Encoded NULL at starting offset %zd", i);
726                 g_string_truncate(s, 0);
727                 goto cleanup;
728             }
729             g_string_append_c(s, tmp);
730             i += 2;
731         } else {
732             g_string_append_c(s, str[i]);
733         }
734     }
735     for ( /*nothing*/; i < orig_len; i++) {
736         if (str[i] == '%') {
737             g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
738                 "'%%' found at offset %zd, but fewer than two characters follow it (%zd)", i, orig_len-i-1);
739             g_string_truncate(s, 0);
740             goto cleanup;
741         } else {
742             g_string_append_c(s, str[i]);
743         }
744     }
745
746 cleanup:
747     ret = s->str;
748     g_string_free(s, FALSE);
749     return ret;
750 }
751
752 /* Helper for parse_braced_component; this will turn a single element array
753  * matching /^\d+\.\.\d+$/ into a sequence of numbered array elements. */
754 static GPtrArray *
755 expand_braced_sequence(GPtrArray *arr)
756 {
757     char *elt, *p;
758     char *l, *r;
759     int ldigits, rdigits, ndigits;
760     guint64 start, end;
761     gboolean leading_zero;
762
763     /* check whether the element matches the pattern */
764     /* expand last element of the array only */
765     elt = g_ptr_array_index(arr, arr->len-1);
766     ldigits = 0;
767     for (l = p = elt; *p && g_ascii_isdigit(*p); p++)
768         ldigits++;
769     if (ldigits == 0)
770         return arr;
771     if (*(p++) != '.')
772         return arr;
773     if (*(p++) != '.')
774         return arr;
775     rdigits = 0;
776     for (r = p; *p && g_ascii_isdigit(*p); p++)
777         rdigits++;
778     if (rdigits == 0)
779         return arr;
780     if (*p)
781         return arr;
782
783     /* we have a match, so extract start and end */
784     start = g_ascii_strtoull(l, NULL, 10);
785     end = g_ascii_strtoull(r, NULL, 10);
786     leading_zero = *l == '0';
787     ndigits = MAX(ldigits, rdigits);
788     if (start > end)
789         return arr;
790
791     /* sanity check.. */
792     if (end - start > 100000)
793         return arr;
794
795     /* remove last from the array */
796     g_ptr_array_remove_index(arr, arr->len - 1);
797
798     /* Add new elements */
799     while (start <= end) {
800         if (leading_zero) {
801             g_ptr_array_add(arr, g_strdup_printf("%0*ju",
802                         ndigits, (uintmax_t)start));
803         } else {
804             g_ptr_array_add(arr, g_strdup_printf("%ju", (uintmax_t)start));
805         }
806         start++;
807     }
808
809     return arr;
810 }
811
812 /* Helper for expand_braced_alternates; returns a list of un-escaped strings
813  * for the first "component" of str, where a component is a plain string or a
814  * brace-enclosed set of alternatives.  str is pointing to the first character
815  * of the next component on return. */
816 static GPtrArray *
817 parse_braced_component(char **str)
818 {
819     GPtrArray *result = g_ptr_array_new();
820
821     if (**str == '{') {
822         char *p = (*str)+1;
823         char *local = g_malloc(strlen(*str)+1);
824         char *current = local;
825         char *c = current;
826
827         while (1) {
828             if (*p == '\0' || *p == '{') {
829                 /* unterminated { .. } or extra '{' */
830                 amfree(local);
831                 g_ptr_array_free(result, TRUE);
832                 return NULL;
833             }
834
835             if (*p == '}' || *p == ',') {
836                 *c = '\0';
837                 g_ptr_array_add(result, g_strdup(current));
838                 result = expand_braced_sequence(result);
839                 current = ++c;
840
841                 if (*p == '}')
842                     break;
843                 else
844                     p++;
845             }
846
847             if (*p == '\\') {
848                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
849                     p++;
850             }
851             *(c++) = *(p++);
852         }
853
854         amfree(local);
855
856         if (*p)
857             *str = p+1;
858         else
859             *str = p;
860     } else {
861         /* no braces -- just un-escape a plain string */
862         char *local = g_malloc(strlen(*str)+1);
863         char *r = local;
864         char *p = *str;
865
866         while (*p && *p != '{') {
867             if (*p == '\\') {
868                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
869                     p++;
870             }
871             *(r++) = *(p++);
872         }
873         *r = '\0';
874         g_ptr_array_add(result, local);
875         *str = p;
876     }
877
878     return result;
879 }
880
881 GPtrArray *
882 expand_braced_alternates(
883     char * source)
884 {
885     GPtrArray *rval = g_ptr_array_new();
886
887     g_ptr_array_add(rval, g_strdup(""));
888
889     while (*source) {
890         GPtrArray *new_components;
891         GPtrArray *new_rval;
892         guint i, j;
893
894         new_components = parse_braced_component(&source);
895         if (!new_components) {
896             /* parse error */
897             g_ptr_array_free(rval, TRUE);
898             return NULL;
899         }
900
901         new_rval = g_ptr_array_new();
902
903         /* do a cartesian join of rval and new_components */
904         for (i = 0; i < rval->len; i++) {
905             for (j = 0; j < new_components->len; j++) {
906                 g_ptr_array_add(new_rval, g_strconcat(
907                     g_ptr_array_index(rval, i),
908                     g_ptr_array_index(new_components, j),
909                     NULL));
910             }
911         }
912
913         g_ptr_array_free(rval, TRUE);
914         g_ptr_array_free(new_components, TRUE);
915         rval = new_rval;
916     }
917
918     return rval;
919 }
920
921 char *
922 collapse_braced_alternates(
923     GPtrArray *source)
924 {
925     GString *result = NULL;
926     guint i;
927
928     result = g_string_new("{");
929
930     for (i = 0; i < source->len; i ++) {
931         const char *str = g_ptr_array_index(source, i);
932         char *qstr = NULL;
933
934         if (strchr(str, ',') || strchr(str, '\\') ||
935             strchr(str, '{') || strchr(str, '}')) {
936             const char *s;
937             char *d;
938
939             s = str;
940             qstr = d = g_malloc(strlen(str)*2+1);
941             while (*s) {
942                 if (*s == ',' || *s == '\\' || *s == '{' || *s == '}')
943                     *(d++) = '\\';
944                 *(d++) = *(s++);
945             }
946             *(d++) = '\0';
947         }
948         g_string_append_printf(result, "%s%s", qstr? qstr : str,
949                 (i < source->len-1)? "," : "");
950         if (qstr)
951             g_free(qstr);
952     }
953
954     g_string_append(result, "}");
955     return g_string_free(result, FALSE);
956 }
957
958 /*
959    Return 0 if the following characters are present
960    * ( ) < > [ ] , ; : ! $ \ / "
961    else returns 1
962 */
963
964 int
965 validate_mailto(
966     const char *mailto)
967 {
968     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
969 }
970
971 int copy_file(
972     char  *dst,
973     char  *src,
974     char **errmsg)
975 {
976     int     infd, outfd;
977     int     save_errno;
978     size_t nb;
979     char    buf[32768];
980     char   *quoted;
981
982     if ((infd = open(src, O_RDONLY)) == -1) {
983         save_errno = errno;
984         quoted = quote_string(src);
985         *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
986                             quoted, strerror(save_errno));
987         amfree(quoted);
988         return -1;
989     }
990
991     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
992         save_errno = errno;
993         quoted = quote_string(dst);
994         *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
995                             quoted, strerror(save_errno));
996         amfree(quoted);
997         close(infd);
998         return -1;
999     }
1000
1001     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
1002         if(full_write(outfd,&buf,nb) < nb) {
1003             save_errno = errno;
1004             quoted = quote_string(dst);
1005             *errmsg = vstrallocf(_("Error writing to '%s': %s"),
1006                                 quoted, strerror(save_errno));
1007             amfree(quoted);
1008             close(infd);
1009             close(outfd);
1010             return -1;
1011         }
1012     }
1013
1014     if (errno != 0) {
1015         save_errno = errno;
1016         quoted = quote_string(src);
1017         *errmsg = vstrallocf(_("Error reading from '%s': %s"),
1018                             quoted, strerror(save_errno));
1019         amfree(quoted);
1020         close(infd);
1021         close(outfd);
1022         return -1;
1023     }
1024
1025     close(infd);
1026     close(outfd);
1027     return 0;
1028 }
1029
1030 #ifndef HAVE_READLINE
1031 /*
1032  * simple readline() replacements, used when we don't have readline
1033  * support from the system.
1034  */
1035
1036 char *
1037 readline(
1038     const char *prompt)
1039 {
1040     g_printf("%s", prompt);
1041     fflush(stdout);
1042     fflush(stderr);
1043     return agets(stdin);
1044 }
1045
1046 void 
1047 add_history(
1048     const char *line)
1049 {
1050     (void)line;         /* Quiet unused parameter warning */
1051 }
1052
1053 #endif
1054
1055 /* Order of preference: readdir64(), readdir(). */
1056 #if HAVE_DECL_READDIR64
1057 #  define USE_DIRENT64
1058 #  define USE_READDIR64
1059 #elif HAVE_DECL_READDIR
1060 #  define USE_READDIR
1061 #else
1062 # error No readdir() or readdir64() available!
1063 #endif
1064
1065 char * portable_readdir(DIR* handle) {
1066
1067 #ifdef USE_DIRENT64
1068     struct dirent64 *entry_p;
1069 #else
1070     struct dirent *entry_p;
1071 #endif
1072
1073     static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
1074
1075     g_static_mutex_lock(&mutex);
1076
1077 #ifdef USE_READDIR
1078     entry_p = readdir(handle);
1079 #endif
1080 #ifdef USE_READDIR64
1081     entry_p = readdir64(handle);
1082 #endif
1083
1084     g_static_mutex_unlock(&mutex);
1085     
1086     if (entry_p == NULL)
1087         return NULL;
1088
1089     /* FIXME: According to glibc documentation, d_name may not be
1090        null-terminated in some cases on some very old platforms. Not
1091        sure what to do about that case. */
1092     return strdup(entry_p->d_name);
1093 }
1094
1095 int search_directory(DIR * handle, const char * regex,
1096                      SearchDirectoryFunctor functor, gpointer user_data) {
1097     int rval = 0;
1098     regex_t compiled_regex;
1099     gboolean done = FALSE;
1100
1101     if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
1102         regfree(&compiled_regex);
1103         return -1;
1104     }
1105
1106     rewinddir(handle);
1107
1108     while (!done) {
1109         char * read_name;
1110         int result;
1111         read_name = portable_readdir(handle);
1112         if (read_name == NULL) {
1113             regfree(&compiled_regex);
1114             return rval;
1115         }
1116         result = regexec(&compiled_regex, read_name, 0, NULL, 0);
1117         if (result == 0) {
1118             rval ++;
1119             done = !functor(read_name, user_data);
1120         }
1121         amfree(read_name);
1122     }
1123     regfree(&compiled_regex);
1124     return rval;
1125 }
1126
1127 char* find_regex_substring(const char* base_string, const regmatch_t match) {
1128     char * rval;
1129     int size;
1130
1131     size = match.rm_eo - match.rm_so;
1132     rval = malloc(size+1);
1133     memcpy(rval, base_string + match.rm_so, size);
1134     rval[size] = '\0';
1135
1136     return rval;
1137 }
1138
1139 int compare_possibly_null_strings(const char * a, const char * b) {
1140     if (a == b) {
1141         /* NULL or otherwise, they're the same. */
1142         return 0;
1143     } else if (a == NULL) {
1144         /* b != NULL */
1145         return -1;
1146     } else if (b == NULL) {
1147         /* a != NULL */
1148         return 1;
1149     } else {
1150         /* a != NULL != b */
1151         return strcmp(a, b);
1152     }
1153 }
1154
1155 int
1156 resolve_hostname(const char *hostname,
1157         int socktype,
1158         struct addrinfo **res,
1159         char **canonname)
1160 {
1161     struct addrinfo hints;
1162     struct addrinfo *myres;
1163     int flags = 0;
1164     int result;
1165
1166     if (res) *res = NULL;
1167     if (canonname) {
1168         *canonname = NULL;
1169         flags = AI_CANONNAME;
1170     }
1171
1172 #ifdef AI_ADDRCONFIG
1173     flags |= AI_ADDRCONFIG;
1174 #endif
1175
1176     memset(&hints, 0, sizeof(hints));
1177 #ifdef WORKING_IPV6
1178     /* get any kind of addresss */
1179     hints.ai_family = AF_UNSPEC;
1180 #else
1181     /* even if getaddrinfo supports IPv6, don't let it return
1182      * such an address */
1183     hints.ai_family = AF_INET;
1184 #endif
1185     hints.ai_flags = flags;
1186     hints.ai_socktype = socktype;
1187     result = getaddrinfo(hostname, NULL, &hints, &myres);
1188     if (result != 0) {
1189         return result;
1190     }
1191
1192     if (canonname && myres && myres->ai_canonname) {
1193         *canonname = stralloc(myres->ai_canonname);
1194     }
1195
1196     if (res) {
1197         *res = myres;
1198     } else {
1199         freeaddrinfo(myres);
1200     }
1201
1202     return result;
1203 }
1204
1205 char *
1206 _str_exit_status(
1207     char *subject,
1208     amwait_t status)
1209 {
1210     if (WIFEXITED(status)) {
1211         int exitstatus = WEXITSTATUS(status);
1212         if (exitstatus == 0)
1213             return vstrallocf(_("%s exited normally"), subject);
1214         else
1215             return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
1216     }
1217
1218     if (WIFSIGNALED(status)) {
1219         int signal = WTERMSIG(status);
1220 #ifdef WCOREDUMP
1221         if (WCOREDUMP(status))
1222             return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
1223                 subject, signal);
1224         else
1225 #endif
1226             return vstrallocf(_("%s exited after receiving signal %d"),
1227                 subject, signal);
1228     }
1229
1230     if (WIFSTOPPED(status)) {
1231         int signal = WSTOPSIG(status);
1232         return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
1233             subject, signal);
1234     }
1235
1236 #ifdef WIFCONTINUED
1237     if (WIFCONTINUED(status)) {
1238         return vstrallocf(_("%s was resumed"), subject);
1239     }
1240 #endif
1241
1242     return vstrallocf(_("%s exited in unknown circumstances"), subject);
1243 }
1244
1245 void
1246 check_running_as(running_as_flags who)
1247 {
1248 #ifdef CHECK_USERID
1249     struct passwd *pw;
1250     uid_t uid_me;
1251     uid_t uid_target;
1252     char *uname_me = NULL;
1253     char *uname_target = NULL;
1254     char *dumpuser;
1255
1256     uid_me = getuid();
1257     if ((pw = getpwuid(uid_me)) == NULL) {
1258         error(_("current userid %ld not found in password database"), (long)uid_me);
1259         /* NOTREACHED */
1260     }
1261     uname_me = stralloc(pw->pw_name);
1262
1263 #ifndef SINGLE_USERID
1264     if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
1265         error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
1266                 (long long int)geteuid(), (long long int)uid_me);
1267         /* NOTREACHED */
1268     }
1269 #endif
1270
1271     switch (who & RUNNING_AS_USER_MASK) {
1272         case RUNNING_AS_ANY:
1273             uid_target = uid_me;
1274             uname_target = uname_me;
1275             amfree(uname_me);
1276             return;
1277
1278         case RUNNING_AS_ROOT:
1279             uid_target = 0;
1280             uname_target = "root";
1281             break;
1282
1283         case RUNNING_AS_DUMPUSER_PREFERRED:
1284             dumpuser = getconf_str(CNF_DUMPUSER);
1285             if ((pw = getpwnam(dumpuser)) != NULL &&
1286                     uid_me != pw->pw_uid) {
1287                 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
1288                     uid_me == pw->pw_uid) {
1289                     /* uid == CLIENT_LOGIN: not ideal, but OK */
1290                     dbprintf(_("NOTE: running as '%s', which is the client"
1291                                " user, not the dumpuser ('%s'); forging"
1292                                " on anyway\n"),
1293                              CLIENT_LOGIN, dumpuser);
1294                     uid_target = uid_me; /* force success below */
1295                    break;
1296                 }
1297             }
1298             /* FALLTHROUGH */
1299
1300         case RUNNING_AS_DUMPUSER:
1301             uname_target = getconf_str(CNF_DUMPUSER);
1302             if ((pw = getpwnam(uname_target)) == NULL) {
1303                 error(_("cannot look up dumpuser \"%s\""), uname_target);
1304                 /*NOTREACHED*/
1305             }
1306             uid_target = pw->pw_uid;
1307             break;
1308
1309         case RUNNING_AS_CLIENT_LOGIN:
1310             uname_target = CLIENT_LOGIN;
1311             if ((pw = getpwnam(uname_target)) == NULL) {
1312                 error(_("cannot look up client user \"%s\""), uname_target);
1313                 /*NOTREACHED*/
1314             }
1315             uid_target = pw->pw_uid;
1316             break;
1317
1318         default:
1319             error(_("Unknown check_running_as() call"));
1320             /* NOTREACHED */
1321     }
1322
1323     if (uid_me != uid_target) {
1324         error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
1325         /*NOTREACHED*/
1326     }
1327     amfree(uname_me);
1328
1329 #else
1330     /* Quiet unused variable warning */
1331     (void)who;
1332 #endif
1333 }
1334
1335 int
1336 set_root_privs(int need_root)
1337 {
1338 #ifndef SINGLE_USERID
1339     static gboolean first_call = TRUE;
1340     static uid_t unpriv = 1;
1341
1342     if (first_call) {
1343         /* save the original real userid (that of our invoker) */
1344         unpriv = getuid();
1345
1346         /* and set all of our userids (including, importantly, the saved
1347          * userid) to 0 */
1348         setuid(0);
1349
1350         /* don't need to do this next time */
1351         first_call = FALSE;
1352     }
1353
1354     if (need_root == 1) {
1355         if (geteuid() == 0) return 1; /* already done */
1356
1357         if (seteuid(0) == -1) return 0;
1358         /* (we don't switch the group back) */
1359     } else if (need_root == -1) {
1360         /* make sure the euid is 0 so that we can set the uid */
1361         if (geteuid() != 0) {
1362             if (seteuid(0) == -1) return 0;
1363         }
1364
1365         /* now set the uid to the unprivileged userid */
1366         if (setuid(unpriv) == -1) return 0;
1367     } else {
1368         if (geteuid() != 0) return 1; /* already done */
1369
1370         /* set the *effective* userid only */
1371         if (seteuid(unpriv) == -1) return 0;
1372         if (setegid(getgid()) == -1) return 0;
1373     }
1374 #else
1375     (void)need_root; /* Quiet unused variable warning */
1376 #endif
1377     return 1;
1378 }
1379
1380 int
1381 become_root(void)
1382 {
1383 #ifndef SINGLE_USERID
1384     /* first, set the effective userid to 0 */
1385     if (seteuid(0) == -1) return 0;
1386
1387     /* then, set all of the userids to 0 */
1388     if (setuid(0) == -1) return 0;
1389 #endif
1390     return 1;
1391 }
1392
1393 char *
1394 base64_decode_alloc_string(
1395     char *in)
1396 {
1397     char   *out;
1398     size_t  in_len = strlen(in);
1399     size_t  out_len = 3 * (in_len / 4) + 3;
1400
1401     out = malloc(out_len);
1402     if (!base64_decode(in, in_len, out, &out_len)) {
1403         amfree(out);
1404         return NULL;
1405     }
1406     out[out_len] = '\0';
1407
1408     return out;
1409 }
1410
1411
1412 /* A GHFunc (callback for g_hash_table_foreach),
1413  * Store a property and it's value in an ARGV.
1414  *
1415  * @param key_p: (char *) property name.
1416  * @param value_p: (GSList *) property values list.
1417  * @param user_data_p: (char ***) pointer to ARGV.
1418  */
1419 static void
1420 proplist_add_to_argv(
1421     gpointer key_p,
1422     gpointer value_p,
1423     gpointer user_data_p)
1424 {
1425     char         *property_s = key_p;
1426     property_t   *value_s = value_p;
1427     GPtrArray    *argv_ptr = user_data_p;
1428     GSList       *value;
1429     char         *q, *w, *qprop;
1430
1431     q = stralloc(property_s);
1432     /* convert to lower case */
1433     for (w=q; *w != '\0'; w++) {
1434         *w = tolower(*w);
1435         if (*w == '_')
1436             *w = '-';
1437     }
1438     qprop = stralloc2("--", q);
1439     amfree(q);
1440     for(value=value_s->values; value != NULL; value = value->next) {
1441         g_ptr_array_add(argv_ptr, stralloc(qprop));
1442         g_ptr_array_add(argv_ptr, stralloc((char *)value->data));
1443     }
1444     amfree(qprop);
1445 }
1446
1447 void
1448 property_add_to_argv(
1449     GPtrArray  *argv_ptr,
1450     GHashTable *proplist)
1451 {
1452     g_hash_table_foreach(proplist, &proplist_add_to_argv, argv_ptr);
1453 }
1454
1455
1456 /*
1457  * Process parameters
1458  */
1459
1460 static char *pname = NULL;
1461 static char *ptype = NULL;
1462 static pcontext_t pcontext = CONTEXT_DEFAULT; 
1463
1464 void
1465 set_pname(char *p)
1466 {
1467     pname = newstralloc(pname, p);
1468 }
1469
1470 char *
1471 get_pname(void)
1472 {
1473     if (!pname) pname = stralloc("unknown");
1474     return pname;
1475 }
1476
1477 void
1478 set_ptype(char *p)
1479 {
1480     ptype = newstralloc(ptype, p);
1481 }
1482
1483 char *
1484 get_ptype(void)
1485 {
1486     if (!ptype) ptype = stralloc("unknown");
1487     return ptype;
1488 }
1489
1490 void
1491 set_pcontext(pcontext_t pc)
1492 {
1493     pcontext = pc;
1494 }
1495
1496 pcontext_t
1497 get_pcontext(void)
1498 {
1499     return pcontext;
1500 }
1501
1502 #ifdef __OpenBSD__
1503 void
1504 openbsd_fd_inform(void)
1505 {
1506     int i;
1507     for (i = DATA_FD_OFFSET; i < DATA_FD_OFFSET + DATA_FD_COUNT*2; i++) {
1508         /* a simple fcntl() will cause the library to "look" at this file
1509          * descriptor, which is good enough */
1510         (void)fcntl(i, F_GETFL);
1511     }
1512 }
1513 #endif
1514
1515 void
1516 debug_executing(
1517     GPtrArray *argv_ptr)
1518 {
1519     guint i;
1520     char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0));
1521
1522     for (i = 1; i < argv_ptr->len-1; i++) {
1523         char *arg = g_shell_quote((char *)g_ptr_array_index(argv_ptr, i));
1524         cmdline = vstrextend(&cmdline, " ", arg, NULL);
1525         amfree(arg);
1526     }
1527     g_debug("Executing: %s\n", cmdline);
1528     amfree(cmdline);
1529 }
1530