1a812013b77dd2f3cb50d359b8369614bc29739b
[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 /*
326  * Writes out the entire iovec
327  */
328 ssize_t
329 full_writev(
330     int                 fd,
331     struct iovec *      iov,
332     int                 iovcnt)
333 {
334     ssize_t delta, n, total;
335
336     assert(iov != NULL);
337
338     total = 0;
339     while (iovcnt > 0) {
340         /*
341          * Write the iovec
342          */
343         n = writev(fd, iov, iovcnt);
344         if (n < 0) {
345             if (errno != EINTR)
346                 return (-1);
347         }
348         else if (n == 0) {
349             errno = EIO;
350             return (-1);
351         } else {
352             total += n;
353             /*
354              * Iterate through each iov.  Figure out what we still need
355              * to write out.
356              */
357             for (; n > 0; iovcnt--, iov++) {
358                 /* 'delta' is the bytes written from this iovec */
359                 delta = ((size_t)n < (size_t)iov->iov_len) ? n : (ssize_t)iov->iov_len;
360                 /* subtract from the total num bytes written */
361                 n -= delta;
362                 assert(n >= 0);
363                 /* subtract from this iovec */
364                 iov->iov_len -= delta;
365                 iov->iov_base = (char *)iov->iov_base + delta;
366                 /* if this iovec isn't empty, run the writev again */
367                 if (iov->iov_len > 0)
368                     break;
369             }
370         }
371     }
372     return (total);
373 }
374
375
376 /*
377  * For backward compatibility we are trying for minimal quoting.  Unless ALWAYS
378  * is true, we only quote a string if it contains whitespace or is misquoted...
379  */
380
381 char *
382 quote_string_maybe(
383     const char *str,
384     gboolean always)
385 {
386     char *  s;
387     char *  ret;
388
389     if ((str == NULL) || (*str == '\0')) {
390         ret = stralloc("\"\"");
391     } else if (!always && (match("[:\'\\\"[:space:][:cntrl:]]", str)) == 0) {
392         /*
393          * String does not need to be quoted since it contains
394          * neither whitespace, control or quote characters.
395          */
396         ret = stralloc(str);
397     } else {
398         /*
399          * Allocate maximum possible string length.
400          * (a string of all quotes plus room for leading ", trailing " and NULL)
401          */
402         ret = s = alloc((strlen(str) * 2) + 2 + 1);
403         *(s++) = '"';
404         while (*str != '\0') {
405             if (*str == '\t') {
406                 *(s++) = '\\';
407                 *(s++) = 't';
408                 str++;
409                 continue;
410             } else if (*str == '\n') {
411                 *(s++) = '\\';
412                 *(s++) = 'n';
413                 str++;
414                 continue;
415             } else if (*str == '\r') {
416                 *(s++) = '\\';
417                 *(s++) = 'r';
418                 str++;
419                 continue;
420             } else if (*str == '\f') {
421                 *(s++) = '\\';
422                 *(s++) = 'f';
423                 str++;
424                 continue;
425             } else if (*str == '\\') {
426                 *(s++) = '\\';
427                 *(s++) = '\\';
428                 str++;
429                 continue;
430             }
431             if (*str == '"')
432                 *(s++) = '\\';
433             *(s++) = *(str++);
434         }
435         *(s++) = '"';
436         *s = '\0';
437     }
438     return (ret);
439 }
440
441
442 char *
443 unquote_string(
444     const char *str)
445 {
446     char * ret;
447
448     if ((str == NULL) || (*str == '\0')) {
449         ret = stralloc("");
450     } else {
451         char * in;
452         char * out;
453
454         ret = in = out = stralloc(str);
455         while (*in != '\0') {
456             if (*in == '"') {
457                 in++;
458                 continue;
459             }
460
461             if (*in == '\\') {
462                 in++;
463                 if (*in == 'n') {
464                     in++;
465                     *(out++) = '\n';
466                     continue;
467                 } else if (*in == 't') {
468                     in++;
469                     *(out++) = '\t';
470                     continue;
471                 } else if (*in == 'r') {
472                     in++;
473                     *(out++) = '\r';
474                     continue;
475                 } else if (*in == 'f') {
476                     in++;
477                     *(out++) = '\f';
478                     continue;
479                 } else if (*in >= '0' && *in <= '7') {
480                     char c = 0;
481                     int i = 0;
482
483                     while (i < 3 && *in >= '0' && *in <= '7') {
484                         c = (c << 3) + *(in++) - '0';
485                         i++;
486                     }
487                     if (c)
488                         *(out++) = c;
489                 } else if (*in == '\0') {
490                     /* trailing backslash -- ignore */
491                     break;
492                 }
493             }
494             *(out++) = *(in++);
495         }
496         *out = '\0';
497     }
498     return (ret);
499 }
500
501 gchar **
502 split_quoted_strings(
503     const gchar *string)
504 {
505     char *local;
506     char *start;
507     char *p;
508     char **result;
509     GPtrArray *strs;
510     int iq = 0;
511
512     if (!string)
513         return NULL;
514
515     p = start = local = g_strdup(string);
516     strs = g_ptr_array_new();
517
518     while (*p) {
519         if (!iq && *p == ' ') {
520             *p = '\0';
521             g_ptr_array_add(strs, unquote_string(start));
522             start = p+1;
523         } else if (*p == '\\') {
524             /* next character is taken literally; if it's a multicharacter
525              * escape (e.g., \171), that doesn't bother us here */
526             p++;
527             if (!*p) break;
528         } else if (*p == '\"') {
529             iq = ! iq;
530         }
531
532         p++;
533     }
534     if (start != string)
535         g_ptr_array_add(strs, unquote_string(start));
536
537     /* now convert strs into a strv, by stealing its references to the underlying
538      * strings */
539     result = g_new0(char *, strs->len + 1);
540     memmove(result, strs->pdata, sizeof(char *) * strs->len);
541
542     g_ptr_array_free(strs, FALSE); /* FALSE => don't free strings */
543     g_free(local);
544
545     return result;
546 }
547
548 char *
549 strquotedstr(char **saveptr)
550 {
551     char *  tok = strtok_r(NULL, " ", saveptr);
552     size_t      len;
553     int         in_quote;
554     int         in_backslash;
555     char       *p, *t;
556
557     if (!tok)
558         return tok;
559     len = strlen(tok);
560     in_quote = 0;
561     in_backslash = 0;
562     p = tok;
563     while (in_quote || in_backslash || *p != '\0') {
564         if (*p == '\0') {
565             /* append a new token */
566             t = strtok_r(NULL, " ", saveptr);
567             if (!t)
568                 return NULL;
569             tok[len] = ' ';
570             len = strlen(tok);
571         }
572         if (!in_backslash) {
573             if (*p == '"')
574                 in_quote = !in_quote;
575             else if (*p == '\\') {
576                 in_backslash = 1;
577             }
578         } else {
579            in_backslash = 0;
580         }
581         p++;
582     }
583     return tok;
584 }
585
586 char *
587 sanitize_string(
588     const char *str)
589 {
590     char * s;
591     char * ret;
592
593     if ((str == NULL) || (*str == '\0')) {
594         ret = stralloc("");
595     } else {
596         ret = stralloc(str);
597         for (s = ret; *s != '\0'; s++) {
598             if (iscntrl((int)*s))
599                 *s = '?';
600         }
601     }
602     return (ret);
603 }
604
605 char *hexencode_string(const char *str)
606 {
607     size_t orig_len, new_len, i;
608     GString *s;
609     gchar *ret;
610     if (!str) {
611         s = g_string_sized_new(0);
612         goto cleanup;
613     }
614     new_len = orig_len = strlen(str);
615     for (i = 0; i < orig_len; i++) {
616         if (!g_ascii_isalnum(str[i])) {
617             new_len += 2;
618         }
619     }
620     s = g_string_sized_new(new_len);
621
622     for (i = 0; i < orig_len; i++) {
623         if (g_ascii_isalnum(str[i])) {
624             g_string_append_c(s, str[i]);
625         } else {
626             g_string_append_printf(s, "%%%02hhx", str[i]);
627         }
628     }
629
630 cleanup:
631     ret = s->str;
632     g_string_free(s, FALSE);
633     return ret;
634 }
635
636 char *hexdecode_string(const char *str, GError **err)
637 {
638     size_t orig_len, new_len, i;
639     GString *s;
640     gchar *ret;
641     if (!str) {
642         s = g_string_sized_new(0);
643         goto cleanup;
644     }
645     new_len = orig_len = strlen(str);
646     for (i = 0; i < orig_len; i++) {
647         if (str[i] == '%') {
648             new_len -= 2;
649         }
650     }
651     s = g_string_sized_new(new_len);
652
653     for (i = 0; (orig_len > 2) && (i < orig_len-2); i++) {
654         if (str[i] == '%') {
655             gchar tmp = 0;
656             size_t j;
657             for (j = 1; j < 3; j++) {
658                 tmp <<= 4;
659                 if (str[i+j] >= '0' && str[i+j] <= '9') {
660                     tmp += str[i+j] - '0';
661                 } else if (str[i+j] >= 'a' && str[i+j] <= 'f') {
662                     tmp += str[i+j] - 'a' + 10;
663                 } else if (str[i+j] >= 'A' && str[i+j] <= 'F') {
664                     tmp += str[i+j] - 'A' + 10;
665                 } else {
666                     /* error */
667                     g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
668                         "Illegal character (non-hex) 0x%02hhx at offset %zd", str[i+j], i+j);
669                     g_string_truncate(s, 0);
670                     goto cleanup;
671                 }
672             }
673             if (!tmp) {
674                 g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
675                     "Encoded NULL at starting offset %zd", i);
676                 g_string_truncate(s, 0);
677                 goto cleanup;
678             }
679             g_string_append_c(s, tmp);
680             i += 2;
681         } else {
682             g_string_append_c(s, str[i]);
683         }
684     }
685     for ( /*nothing*/; i < orig_len; i++) {
686         if (str[i] == '%') {
687             g_set_error(err, am_util_error_quark(), AM_UTIL_ERROR_HEXDECODEINVAL,
688                 "'%%' found at offset %zd, but fewer than two characters follow it (%zd)", i, orig_len-i-1);
689             g_string_truncate(s, 0);
690             goto cleanup;
691         } else {
692             g_string_append_c(s, str[i]);
693         }
694     }
695
696 cleanup:
697     ret = s->str;
698     g_string_free(s, FALSE);
699     return ret;
700 }
701
702 /* Helper for expand_braced_alternates; returns a list of un-escaped strings
703  * for the first "component" of str, where a component is a plain string or a
704  * brace-enclosed set of alternatives.  str is pointing to the first character
705  * of the next component on return. */
706 static GPtrArray *
707 parse_braced_component(char **str)
708 {
709     GPtrArray *result = g_ptr_array_new();
710
711     if (**str == '{') {
712         char *p = (*str)+1;
713         char *local = g_malloc(strlen(*str)+1);
714         char *current = local;
715         char *c = current;
716
717         while (1) {
718             if (*p == '\0' || *p == '{') {
719                 /* unterminated { .. } or extra '{' */
720                 amfree(local);
721                 g_ptr_array_free(result, TRUE);
722                 return NULL;
723             }
724
725             if (*p == '}' || *p == ',') {
726                 *c = '\0';
727                 g_ptr_array_add(result, g_strdup(current));
728                 current = ++c;
729
730                 if (*p == '}')
731                     break;
732                 else
733                     p++;
734             }
735
736             if (*p == '\\') {
737                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
738                     p++;
739             }
740             *(c++) = *(p++);
741         }
742
743         amfree(local);
744
745         if (*p)
746             *str = p+1;
747         else
748             *str = p;
749     } else {
750         /* no braces -- just un-escape a plain string */
751         char *local = g_malloc(strlen(*str)+1);
752         char *r = local;
753         char *p = *str;
754
755         while (*p && *p != '{') {
756             if (*p == '\\') {
757                 if (*(p+1) == '{' || *(p+1) == '}' || *(p+1) == '\\' || *(p+1) == ',')
758                     p++;
759             }
760             *(r++) = *(p++);
761         }
762         *r = '\0';
763         g_ptr_array_add(result, local);
764         *str = p;
765     }
766
767     return result;
768 }
769
770 GPtrArray *
771 expand_braced_alternates(
772     char * source)
773 {
774     GPtrArray *rval = g_ptr_array_new();
775
776     g_ptr_array_add(rval, g_strdup(""));
777
778     while (*source) {
779         GPtrArray *new_components;
780         GPtrArray *new_rval;
781         guint i, j;
782
783         new_components = parse_braced_component(&source);
784         if (!new_components) {
785             /* parse error */
786             g_ptr_array_free(rval, TRUE);
787             return NULL;
788         }
789
790         new_rval = g_ptr_array_new();
791
792         /* do a cartesian join of rval and new_components */
793         for (i = 0; i < rval->len; i++) {
794             for (j = 0; j < new_components->len; j++) {
795                 g_ptr_array_add(new_rval, g_strconcat(
796                     g_ptr_array_index(rval, i),
797                     g_ptr_array_index(new_components, j),
798                     NULL));
799             }
800         }
801
802         g_ptr_array_free(rval, TRUE);
803         g_ptr_array_free(new_components, TRUE);
804         rval = new_rval;
805     }
806
807     return rval;
808 }
809
810 char *
811 collapse_braced_alternates(
812     GPtrArray *source)
813 {
814     GString *result = NULL;
815     guint i;
816
817     result = g_string_new("{");
818
819     for (i = 0; i < source->len; i ++) {
820         const char *str = g_ptr_array_index(source, i);
821         char *qstr = NULL;
822
823         if (strchr(str, ',') || strchr(str, '\\') ||
824             strchr(str, '{') || strchr(str, '}')) {
825             const char *s;
826             char *d;
827
828             s = str;
829             qstr = d = g_malloc(strlen(str)*2+1);
830             while (*s) {
831                 if (*s == ',' || *s == '\\' || *s == '{' || *s == '}')
832                     *(d++) = '\\';
833                 *(d++) = *(s++);
834             }
835             *(d++) = '\0';
836         }
837         g_string_append_printf(result, "%s%s", qstr? qstr : str,
838                 (i < source->len-1)? "," : "");
839         if (qstr)
840             g_free(qstr);
841     }
842
843     g_string_append(result, "}");
844     return g_string_free(result, FALSE);
845 }
846
847 /*
848    Return 0 if the following characters are present
849    * ( ) < > [ ] , ; : ! $ \ / "
850    else returns 1
851 */
852
853 int
854 validate_mailto(
855     const char *mailto)
856 {
857     return !match("\\*|<|>|\\(|\\)|\\[|\\]|,|;|:|\\\\|/|\"|\\!|\\$|\\|", mailto);
858 }
859
860 int copy_file(
861     char  *dst,
862     char  *src,
863     char **errmsg)
864 {
865     int     infd, outfd;
866     int     save_errno;
867     size_t nb;
868     char    buf[32768];
869     char   *quoted;
870
871     if ((infd = open(src, O_RDONLY)) == -1) {
872         save_errno = errno;
873         quoted = quote_string(src);
874         *errmsg = vstrallocf(_("Can't open file '%s' for reading: %s"),
875                             quoted, strerror(save_errno));
876         amfree(quoted);
877         return -1;
878     }
879
880     if ((outfd = open(dst, O_WRONLY|O_CREAT, 0600)) == -1) {
881         save_errno = errno;
882         quoted = quote_string(dst);
883         *errmsg = vstrallocf(_("Can't open file '%s' for writting: %s"),
884                             quoted, strerror(save_errno));
885         amfree(quoted);
886         close(infd);
887         return -1;
888     }
889
890     while((nb=read(infd, &buf, SIZEOF(buf))) > 0) {
891         if(full_write(outfd,&buf,nb) < nb) {
892             save_errno = errno;
893             quoted = quote_string(dst);
894             *errmsg = vstrallocf(_("Error writing to '%s': %s"),
895                                 quoted, strerror(save_errno));
896             amfree(quoted);
897             close(infd);
898             close(outfd);
899             return -1;
900         }
901     }
902
903     if (errno != 0) {
904         save_errno = errno;
905         quoted = quote_string(src);
906         *errmsg = vstrallocf(_("Error reading from '%s': %s"),
907                             quoted, strerror(save_errno));
908         amfree(quoted);
909         close(infd);
910         close(outfd);
911         return -1;
912     }
913
914     close(infd);
915     close(outfd);
916     return 0;
917 }
918
919 #ifndef HAVE_READLINE
920 /*
921  * simple readline() replacements, used when we don't have readline
922  * support from the system.
923  */
924
925 char *
926 readline(
927     const char *prompt)
928 {
929     g_printf("%s", prompt);
930     fflush(stdout);
931     fflush(stderr);
932     return agets(stdin);
933 }
934
935 void 
936 add_history(
937     const char *line)
938 {
939     (void)line;         /* Quiet unused parameter warning */
940 }
941
942 #endif
943
944 /* Order of preference: readdir64(), readdir(). */
945 #if HAVE_DECL_READDIR64
946 #  define USE_DIRENT64
947 #  define USE_READDIR64
948 #elif HAVE_DECL_READDIR
949 #  define USE_READDIR
950 #else
951 # error No readdir() or readdir64() available!
952 #endif
953
954 char * portable_readdir(DIR* handle) {
955
956 #ifdef USE_DIRENT64
957     struct dirent64 *entry_p;
958 #else
959     struct dirent *entry_p;
960 #endif
961
962     static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
963
964     g_static_mutex_lock(&mutex);
965
966 #ifdef USE_READDIR
967     entry_p = readdir(handle);
968 #endif
969 #ifdef USE_READDIR64
970     entry_p = readdir64(handle);
971 #endif
972
973     g_static_mutex_unlock(&mutex);
974     
975     if (entry_p == NULL)
976         return NULL;
977
978     /* FIXME: According to glibc documentation, d_name may not be
979        null-terminated in some cases on some very old platforms. Not
980        sure what to do about that case. */
981     return strdup(entry_p->d_name);
982 }
983
984 int search_directory(DIR * handle, const char * regex,
985                      SearchDirectoryFunctor functor, gpointer user_data) {
986     int rval = 0;
987     regex_t compiled_regex;
988     gboolean done = FALSE;
989
990     if (regcomp(&compiled_regex, regex, REG_EXTENDED | REG_NOSUB) != 0) {
991         regfree(&compiled_regex);
992         return -1;
993     }
994
995     rewinddir(handle);
996
997     while (!done) {
998         char * read_name;
999         int result;
1000         read_name = portable_readdir(handle);
1001         if (read_name == NULL) {
1002             regfree(&compiled_regex);
1003             return rval;
1004         }
1005         result = regexec(&compiled_regex, read_name, 0, NULL, 0);
1006         if (result == 0) {
1007             rval ++;
1008             done = !functor(read_name, user_data);
1009         }
1010         amfree(read_name);
1011     }
1012     regfree(&compiled_regex);
1013     return rval;
1014 }
1015
1016 char* find_regex_substring(const char* base_string, const regmatch_t match) {
1017     char * rval;
1018     int size;
1019
1020     size = match.rm_eo - match.rm_so;
1021     rval = malloc(size+1);
1022     memcpy(rval, base_string + match.rm_so, size);
1023     rval[size] = '\0';
1024
1025     return rval;
1026 }
1027
1028 int compare_possibly_null_strings(const char * a, const char * b) {
1029     if (a == b) {
1030         /* NULL or otherwise, they're the same. */
1031         return 0;
1032     } else if (a == NULL) {
1033         /* b != NULL */
1034         return -1;
1035     } else if (b == NULL) {
1036         /* a != NULL */
1037         return 1;
1038     } else {
1039         /* a != NULL != b */
1040         return strcmp(a, b);
1041     }
1042 }
1043
1044 int
1045 resolve_hostname(const char *hostname,
1046         int socktype,
1047         struct addrinfo **res,
1048         char **canonname)
1049 {
1050     struct addrinfo hints;
1051     struct addrinfo *myres;
1052     int flags = 0;
1053     int result;
1054
1055     if (res) *res = NULL;
1056     if (canonname) {
1057         *canonname = NULL;
1058         flags = AI_CANONNAME;
1059     }
1060
1061 #ifdef AI_ADDRCONFIG
1062     flags |= AI_ADDRCONFIG;
1063 #endif
1064
1065     memset(&hints, 0, sizeof(hints));
1066 #ifdef WORKING_IPV6
1067     /* get any kind of addresss */
1068     hints.ai_family = AF_UNSPEC;
1069 #else
1070     /* even if getaddrinfo supports IPv6, don't let it return
1071      * such an address */
1072     hints.ai_family = AF_INET;
1073 #endif
1074     hints.ai_flags = flags;
1075     hints.ai_socktype = socktype;
1076     result = getaddrinfo(hostname, NULL, &hints, &myres);
1077     if (result != 0) {
1078         return result;
1079     }
1080
1081     if (canonname && myres && myres->ai_canonname) {
1082         *canonname = stralloc(myres->ai_canonname);
1083     }
1084
1085     if (res) {
1086         *res = myres;
1087     } else {
1088         freeaddrinfo(myres);
1089     }
1090
1091     return result;
1092 }
1093
1094 char *
1095 _str_exit_status(
1096     char *subject,
1097     amwait_t status)
1098 {
1099     if (WIFEXITED(status)) {
1100         int exitstatus = WEXITSTATUS(status);
1101         if (exitstatus == 0)
1102             return vstrallocf(_("%s exited normally"), subject);
1103         else
1104             return vstrallocf(_("%s exited with status %d"), subject, exitstatus);
1105     }
1106
1107     if (WIFSIGNALED(status)) {
1108         int signal = WTERMSIG(status);
1109 #ifdef WCOREDUMP
1110         if (WCOREDUMP(status))
1111             return vstrallocf(_("%s exited after receiving signal %d (core dumped)"),
1112                 subject, signal);
1113         else
1114 #endif
1115             return vstrallocf(_("%s exited after receiving signal %d"),
1116                 subject, signal);
1117     }
1118
1119     if (WIFSTOPPED(status)) {
1120         int signal = WSTOPSIG(status);
1121         return vstrallocf(_("%s stopped temporarily after receiving signal %d"),
1122             subject, signal);
1123     }
1124
1125 #ifdef WIFCONTINUED
1126     if (WIFCONTINUED(status)) {
1127         return vstrallocf(_("%s was resumed"), subject);
1128     }
1129 #endif
1130
1131     return vstrallocf(_("%s exited in unknown circumstances"), subject);
1132 }
1133
1134 void
1135 check_running_as(running_as_flags who)
1136 {
1137 #ifdef CHECK_USERID
1138     struct passwd *pw;
1139     uid_t uid_me;
1140     uid_t uid_target;
1141     char *uname_me = NULL;
1142     char *uname_target = NULL;
1143     char *dumpuser;
1144
1145     uid_me = getuid();
1146     if ((pw = getpwuid(uid_me)) == NULL) {
1147         error(_("current userid %ld not found in password database"), (long)uid_me);
1148         /* NOTREACHED */
1149     }
1150     uname_me = stralloc(pw->pw_name);
1151
1152 #ifndef SINGLE_USERID
1153     if (!(who & RUNNING_AS_UID_ONLY) && uid_me != geteuid()) {
1154         error(_("euid (%lld) does not match uid (%lld); is this program setuid-root when it shouldn't be?"),
1155                 (long long int)geteuid(), (long long int)uid_me);
1156         /* NOTREACHED */
1157     }
1158 #endif
1159
1160     switch (who & RUNNING_AS_USER_MASK) {
1161         case RUNNING_AS_ANY:
1162             uid_target = uid_me;
1163             uname_target = uname_me;
1164             amfree(uname_me);
1165             return;
1166
1167         case RUNNING_AS_ROOT:
1168             uid_target = 0;
1169             uname_target = "root";
1170             break;
1171
1172         case RUNNING_AS_DUMPUSER_PREFERRED:
1173             dumpuser = getconf_str(CNF_DUMPUSER);
1174             if ((pw = getpwnam(dumpuser)) != NULL &&
1175                     uid_me != pw->pw_uid) {
1176                 if ((pw = getpwnam(CLIENT_LOGIN)) != NULL &&
1177                     uid_me == pw->pw_uid) {
1178                     /* uid == CLIENT_LOGIN: not ideal, but OK */
1179                     dbprintf(_("NOTE: running as '%s', which is the client"
1180                                " user, not the dumpuser ('%s'); forging"
1181                                " on anyway\n"),
1182                              CLIENT_LOGIN, dumpuser);
1183                     uid_target = uid_me; /* force success below */
1184                    break;
1185                 }
1186             }
1187             /* FALLTHROUGH */
1188
1189         case RUNNING_AS_DUMPUSER:
1190             uname_target = getconf_str(CNF_DUMPUSER);
1191             if ((pw = getpwnam(uname_target)) == NULL) {
1192                 error(_("cannot look up dumpuser \"%s\""), uname_target);
1193                 /*NOTREACHED*/
1194             }
1195             uid_target = pw->pw_uid;
1196             break;
1197
1198         case RUNNING_AS_CLIENT_LOGIN:
1199             uname_target = CLIENT_LOGIN;
1200             if ((pw = getpwnam(uname_target)) == NULL) {
1201                 error(_("cannot look up client user \"%s\""), uname_target);
1202                 /*NOTREACHED*/
1203             }
1204             uid_target = pw->pw_uid;
1205             break;
1206
1207         default:
1208             error(_("Unknown check_running_as() call"));
1209             /* NOTREACHED */
1210     }
1211
1212     if (uid_me != uid_target) {
1213         error(_("running as user \"%s\" instead of \"%s\""), uname_me, uname_target);
1214         /*NOTREACHED*/
1215     }
1216     amfree(uname_me);
1217
1218 #else
1219     /* Quiet unused variable warning */
1220     (void)who;
1221 #endif
1222 }
1223
1224 int
1225 set_root_privs(int need_root)
1226 {
1227 #ifndef SINGLE_USERID
1228     static gboolean first_call = TRUE;
1229     static uid_t unpriv = 1;
1230
1231     if (first_call) {
1232         /* save the original real userid (that of our invoker) */
1233         unpriv = getuid();
1234
1235         /* and set all of our userids (including, importantly, the saved
1236          * userid) to 0 */
1237         setuid(0);
1238
1239         /* don't need to do this next time */
1240         first_call = FALSE;
1241     }
1242
1243     if (need_root == 1) {
1244         if (geteuid() == 0) return 1; /* already done */
1245
1246         if (seteuid(0) == -1) return 0;
1247         /* (we don't switch the group back) */
1248     } else if (need_root == -1) {
1249         /* make sure the euid is 0 so that we can set the uid */
1250         if (geteuid() != 0) {
1251             if (seteuid(0) == -1) return 0;
1252         }
1253
1254         /* now set the uid to the unprivileged userid */
1255         if (setuid(unpriv) == -1) return 0;
1256     } else {
1257         if (geteuid() != 0) return 1; /* already done */
1258
1259         /* set the *effective* userid only */
1260         if (seteuid(unpriv) == -1) return 0;
1261         if (setegid(getgid()) == -1) return 0;
1262     }
1263 #else
1264     (void)need_root; /* Quiet unused variable warning */
1265 #endif
1266     return 1;
1267 }
1268
1269 int
1270 become_root(void)
1271 {
1272 #ifndef SINGLE_USERID
1273     /* first, set the effective userid to 0 */
1274     if (seteuid(0) == -1) return 0;
1275
1276     /* then, set all of the userids to 0 */
1277     if (setuid(0) == -1) return 0;
1278 #endif
1279     return 1;
1280 }
1281
1282 char *
1283 base64_decode_alloc_string(
1284     char *in)
1285 {
1286     char   *out;
1287     size_t  in_len = strlen(in);
1288     size_t  out_len = 3 * (in_len / 4) + 3;
1289
1290     out = malloc(out_len);
1291     if (!base64_decode(in, in_len, out, &out_len)) {
1292         amfree(out);
1293         return NULL;
1294     }
1295     out[out_len] = '\0';
1296
1297     return out;
1298 }
1299
1300
1301 /* A GHFunc (callback for g_hash_table_foreach) */
1302 void count_proplist(
1303     gpointer key_p G_GNUC_UNUSED,
1304     gpointer value_p,
1305     gpointer user_data_p)
1306 {
1307     property_t *value_s = value_p;
1308     int    *nb = user_data_p;
1309     GSList  *value;
1310
1311     for(value=value_s->values; value != NULL; value = value->next) {
1312         (*nb)++;
1313     }
1314 }
1315
1316 /* A GHFunc (callback for g_hash_table_foreach) */
1317 void proplist_add_to_argv(
1318     gpointer key_p,
1319     gpointer value_p,
1320     gpointer user_data_p)
1321 {
1322     char         *property_s = key_p;
1323     property_t   *value_s = value_p;
1324     GPtrArray    *argv_ptr = user_data_p;
1325     GSList       *value;
1326     char         *q, *w, *qprop;
1327
1328     q = stralloc(property_s);
1329     /* convert to lower case */
1330     for (w=q; *w != '\0'; w++) {
1331         *w = tolower(*w);
1332         if (*w == '_')
1333             *w = '-';
1334     }
1335     qprop = stralloc2("--", q);
1336     amfree(q);
1337     for(value=value_s->values; value != NULL; value = value->next) {
1338         g_ptr_array_add(argv_ptr, stralloc(qprop));
1339         g_ptr_array_add(argv_ptr, stralloc((char *)value->data));
1340     }
1341     amfree(qprop);
1342 }
1343
1344
1345 /*
1346  * Process parameters
1347  */
1348
1349 static char *pname = NULL;
1350 static char *ptype = NULL;
1351 static pcontext_t pcontext = CONTEXT_DEFAULT; 
1352
1353 void
1354 set_pname(char *p)
1355 {
1356     pname = newstralloc(pname, p);
1357 }
1358
1359 char *
1360 get_pname(void)
1361 {
1362     if (!pname) pname = stralloc("unknown");
1363     return pname;
1364 }
1365
1366 void
1367 set_ptype(char *p)
1368 {
1369     ptype = newstralloc(ptype, p);
1370 }
1371
1372 char *
1373 get_ptype(void)
1374 {
1375     if (!ptype) ptype = stralloc("unknown");
1376     return ptype;
1377 }
1378
1379 void
1380 set_pcontext(pcontext_t pc)
1381 {
1382     pcontext = pc;
1383 }
1384
1385 pcontext_t
1386 get_pcontext(void)
1387 {
1388     return pcontext;
1389 }
1390
1391 #ifdef __OpenBSD__
1392 void
1393 openbsd_fd_inform(void)
1394 {
1395     int i;
1396     for (i = DATA_FD_OFFSET; i < DATA_FD_OFFSET + DATA_FD_COUNT*2; i++) {
1397         /* a simple fcntl() will cause the library to "look" at this file
1398          * descriptor, which is good enough */
1399         (void)fcntl(i, F_GETFL);
1400     }
1401 }
1402 #endif
1403
1404 void
1405 debug_executing(
1406     GPtrArray *argv_ptr)
1407 {
1408     guint i;
1409     char *cmdline = stralloc((char *)g_ptr_array_index(argv_ptr, 0));
1410
1411     for (i = 1; i < argv_ptr->len-1; i++) {
1412         char *arg = g_shell_quote((char *)g_ptr_array_index(argv_ptr, i));
1413         cmdline = vstrextend(&cmdline, " ", arg, NULL);
1414         amfree(arg);
1415     }
1416     g_debug("Executing: %s\n", cmdline);
1417     amfree(cmdline);
1418 }
1419