Add STATUS_MODIFIED.
[pwmd.git] / src / util-misc.c
blob867b966058f7cd7d7850fa575d66527e8bcba6c3
1 /*
2 Copyright (C) 2006-2022 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 #include <ctype.h>
34 #include <pwd.h>
36 #include "pwmd-error.h"
37 #include <gcrypt.h>
38 #include "util-misc.h"
39 #include "util-string.h"
40 #include "mutex.h"
41 #include "cache.h"
42 #include "mem.h"
44 char *home_directory;
45 void log_write (const char *fmt, ...);
47 int
48 valid_filename (const char *filename)
50 const char *p;
52 if (!filename || !*filename)
53 return 0;
55 if (filename[0] == '-' && filename[1] == 0)
56 return 0;
58 for (p = filename; *p; p++)
60 if (*p == '/' || isspace (*p))
61 return 0;
64 return 1;
67 /* If 'user' is null then lookup entry for 'uid'. Caller must free 'buf'. */
68 struct passwd *
69 get_pwd_struct (const char *user, uid_t uid, struct passwd *pw, char **buf,
70 gpg_error_t *rc)
72 struct passwd *result = NULL;
73 #ifdef HAVE_GETPWNAM_R
74 size_t len;
75 int err = 0;
77 len = sysconf (_SC_GETPW_R_SIZE_MAX);
78 if (len == -1)
79 len = 16384;
81 *buf = xmalloc (len);
82 if (!*buf)
83 return NULL;
85 if (user)
86 err = getpwnam_r (user, pw, *buf, len, &result);
87 else
88 err = getpwuid_r (uid, pw, *buf, len, &result);
90 if (!err && result)
91 return result;
93 xfree (*buf);
94 *buf = NULL;
95 errno = err;
96 *rc = gpg_error_from_errno (err);
97 return NULL;
98 #else
99 if (buf)
100 *buf = NULL;
102 errno = 0;
103 if (user)
104 result = getpwnam (user);
105 else
106 result = getpwuid (uid);
108 if (!result)
109 *rc = gpg_error_from_syserror ();
111 return result;
112 #endif
115 static char *
116 get_pwd_entry(uid_t uid, int which)
118 gpg_error_t rc;
119 char *buf;
120 struct passwd pw;
121 struct passwd *result = get_pwd_struct (NULL, uid, &pw, &buf, &rc);
122 char *value = NULL;
124 if (!result)
125 return NULL;
127 if (result)
129 if (!which)
130 value = str_dup (result->pw_dir);
131 else
132 value = str_dup (result->pw_name);
135 xfree (buf);
136 return value;
139 char *
140 get_username (uid_t uid)
142 return get_pwd_entry (uid, 1);
145 char *
146 get_home_dir ()
148 if (home_directory)
149 return home_directory;
151 home_directory = get_pwd_entry (getuid(), 0);
152 return home_directory;
155 char *
156 expand_homedir (char *str)
158 char *p = str;
160 if (*p++ == '~')
162 char *dir = get_pwd_entry(getuid(), 0);
163 char *s;
165 if (!dir)
166 return NULL;
168 s = str_asprintf ("%s%s", dir, p);
169 xfree (dir);
170 return s;
173 return str_dup (str);
176 /* The 'more' parameter lets the calling command process remaining
177 * non-option arguments. */
178 gpg_error_t
179 parse_options (char **line, struct argv_s * args[], void *data, int more)
181 char *p = *line;
182 gpg_error_t rc = 0;
184 for (; p && *p; p++)
186 while (*p == ' ')
187 p++;
189 if (!*p)
190 break;
192 if (*p == '-' && *(p + 1) == '-')
194 p += 2;
195 char opt[255] = { 0 }, value[255] =
198 char *tp;
199 unsigned len;
201 if (!*p || *p == ' ')
203 if (*p)
204 p++;
206 break;
209 for (tp = opt, len = 0; *p; p++, len++)
211 if (len + 1 == 255)
212 return GPG_ERR_LINE_TOO_LONG;
214 if (*p == ' ' || *p == '=')
216 *tp = 0;
218 if (*p == '=')
220 int inquote = 0;
222 p++;
224 for (tp = value, len = 0; *p; p++, len++)
226 if (len + 1 == 255)
227 return GPG_ERR_LINE_TOO_LONG;
229 if (*p == '\"')
231 inquote = !inquote;
232 continue;
234 else if (*p == ' ' && !inquote)
235 break;
237 *tp++ = *p;
240 *tp = 0;
243 break;
246 *tp++ = *p;
249 *tp = 0;
250 int match = 0;
252 for (int i = 0; args[i]; i++)
254 if (!strcmp (args[i]->opt, opt))
256 log_write2 ("param: name='%s' value='%s'", opt, value);
257 if (args[i]->type == OPTION_TYPE_NOARG && *value)
258 return GPG_ERR_SYNTAX;
259 else if (args[i]->type == OPTION_TYPE_ARG && !*value)
260 return GPG_ERR_SYNTAX;
262 if (args[i]->func)
264 rc = args[i]->func (data, value);
265 if (rc)
266 return rc;
269 match = 1;
270 break;
274 if (!match)
275 return GPG_ERR_UNKNOWN_OPTION;
277 if (!*p)
278 break;
280 continue;
283 if (*(p+1) && !more)
284 return GPG_ERR_SYNTAX;
286 break;
289 *line = p;
290 return rc;
293 char *
294 bin2hex (const unsigned char *data, size_t len)
296 size_t size = len * 2 + 1;
297 char buf[size];
298 size_t n;
300 buf[0] = 0;
302 for (n = 0; n < len; n++)
304 char c[3];
306 sprintf (c, "%02X", data[n]);
307 strcat (buf, c);
310 return str_dup (buf);
313 static char *
314 html_escape (const char *line, size_t len, int space)
316 struct string_s *string = string_new ("");
317 size_t n;
318 char *tmp;
320 if (!string)
321 return NULL;
323 for (n = 0; n < len; n++)
325 switch (line[n])
327 case '<':
328 string = string_append (string, "&lt;");
329 break;
330 case '>':
331 string = string_append (string, "&gt;");
332 break;
333 case ' ':
334 if (space)
336 string = string_append (string, "%20");
337 break;
339 default:
340 string = string_append_printf (string, "%c", line[n]);
341 break;
345 tmp = string->str;
346 string_free (string, 0);
347 return tmp;
350 static char *
351 strip_texi_html (const char *line)
353 size_t len;
354 const char *p;
355 char *tmp;
356 struct string_s *string = string_new ("<html><body>");
358 if (!string)
359 return NULL;
361 /* The command usage text. */
362 p = strchr (line, '\n');
363 if (p)
365 len = strlen (line)-strlen(p);
366 tmp = html_escape (line, len, 0);
367 string = string_append_printf (string, "<b>%s</b><p>", tmp);
368 xfree (tmp);
369 p++;
371 else
372 p = line;
374 for (; p && *p; p++)
376 int non_anchor = 1;
377 int var = 0;
378 int code = 0;
379 int emph = 0;
380 int cite = 0;
382 if (*p == '@' && *(p + 1) != '@')
384 p++;
386 if (!strncasecmp (p, "end ", 4))
388 p += 4;
389 if (!strncasecmp (p, "table", 5))
391 string = string_append (string, "</p>");
393 else if (!strncasecmp (p, "example", 7))
395 string = string_append (string, "</code>");
398 while (*p++ != '\n');
399 p--;
400 continue;
402 else if (!strncasecmp (p, "table ", 6))
404 while (*p++ != '\n');
405 p--;
406 continue;
408 else if (!strncasecmp (p, "example", 7))
410 string = string_append (string, "<code>");
411 while (*p++ != '\n');
412 p--;
413 continue;
415 else if (!strncasecmp (p, "sp ", 3))
417 while (*p++ != '\n');
418 p--;
419 string = string_append (string, "<p>");
420 continue;
422 else if (!strncasecmp (p, "item ", 5))
424 p += 5;
425 string = string_append (string, "<p><b>");
426 tmp = strchr (p, '\n');
427 len = strlen (p) - strlen (tmp);
428 while (len--)
430 if (*p == '<')
432 string = string_append (string, "&lt;");
433 p++;
435 else if (*p == '>')
437 string = string_append (string, "&gt;");
438 p++;
440 else
441 string = string_append_printf (string, "%c", *p++);
444 string = string_append (string, "</b><br/>");
445 continue;
447 else if (!strncasecmp (p, "pxref", 5)
448 || !strncasecmp (p, "npxref", 6))
450 int n = 0;
452 if (!strncasecmp (p, "npxref", 6))
453 n++;
455 p += n ? 7 : 6;
456 non_anchor = 0;
457 string = string_append_printf (string, "%s<a href=\"",
458 n ? "" : "see ");
459 goto close;
461 else if (!strncasecmp (p, "xref", 4))
463 p += 5;
464 non_anchor = 0;
465 string = string_append (string, "See <a href=\"");
466 goto close;
468 else if (!strncasecmp (p, "url", 3))
470 p += 4;
471 non_anchor = 0;
472 string = string_append (string, "<a href=\"");
473 goto close;
475 else if (!strncasecmp (p, "key", 3))
477 p += 4;
478 goto close;
480 else if (!strncasecmp (p, "file", 4))
482 p += 5;
483 var = 1;
484 string = string_append (string, "<var>");
485 goto close;
487 else if (!strncasecmp (p, "var", 3))
489 p += 4;
490 var = 1;
491 string = string_append (string, "<var>");
492 goto close;
494 else if (!strncasecmp (p, "option", 6))
496 p += 7;
497 var = 1;
498 string = string_append (string, "<var>");
499 goto close;
501 else if (!strncasecmp (p, "code", 4))
503 p += 5;
504 code = 1;
505 string = string_append (string, "<b>");
506 goto close;
508 else if (!strncasecmp (p, "emph", 4))
510 p += 5;
511 emph = 1;
512 string = string_append (string, "<em>");
513 goto close;
515 else if (!strncasecmp (p, "command", 7))
517 p += 8;
518 emph = 1;
519 string = string_append (string, "<em>");
520 goto close;
522 else if (!strncasecmp (p, "cite", 4))
524 p += 5;
525 cite = 1;
526 string = string_append (string, "<cite>");
527 goto close;
529 else if (!strncasecmp (p, "abbr", 4))
531 p += 5;
532 emph = 1;
533 string = string_append (string, "<em>");
534 goto close;
536 else if (!strncasecmp (p, "*", 1))
538 string = string_append (string, "<br/>");
539 continue;
542 while (*p && *p != '{')
544 if (*++p == '*')
546 p++;
547 goto append;
551 close:
552 if (*p)
554 char buf [255];
556 tmp = strchr (p, '}');
557 len = strlen (p) - strlen (tmp);
558 tmp = buf;
559 while (len--)
560 *tmp++ = *p++;
562 *tmp = 0;
564 if (*p)
565 p++;
567 if (!non_anchor)
569 tmp = html_escape (buf, strlen (buf), 1);
570 string = string_append_printf (string, "%s\">%s</a>", tmp, buf);
571 xfree (tmp);
573 else
574 string = string_append (string, buf);
576 if (var)
577 string = string_append (string, "</var>");
578 else if (code)
579 string = string_append (string, "</b>");
580 else if (emph)
581 string = string_append (string, "</em>");
582 else if (cite)
583 string = string_append (string, "</cite>");
586 else if (*p == '@' && *(p + 1) == '@')
587 p++;
589 append:
590 string = string_append_printf (string, "%c", *p);
592 if (!*p)
593 break;
596 string = string_append (string, "</body></html>");
597 tmp = string->str;
598 string_free (string, 0);
599 return tmp;
602 static char *
603 strip_texi (const char *str, int html)
605 const char *p;
606 char *s;
607 int c = 0;
609 if (html)
610 return strip_texi_html (str);
612 s = str_dup (str);
613 if (!s)
614 return NULL;
616 for (p = str; *p; p++)
618 if (*p == '@' && *(p + 1) != '@')
620 p++;
622 if (!strncasecmp (p, "table", 5)
623 || !strncasecmp (p, "end ", 4))
625 while (*p++ != '\n');
626 p--;
627 continue;
629 else if (!strncasecmp (p, "example", 7)
630 || !strncasecmp (p, "end ", 4))
632 while (*p++ != '\n');
633 p--;
634 continue;
636 else if (!strncasecmp (p, "sp ", 3))
638 while (*p++ != '\n');
639 p--;
640 continue;
642 else if (!strncasecmp (p, "item", 4))
644 p += 5;
645 while (*p && *p != '\n')
646 s[c++] = *p++;
648 s[c++] = '\n';
649 continue;
651 else if (!strncasecmp (p, "pxref", 5))
653 p += 5;
654 strcat (s, "see ");
655 c = strlen (s);
656 goto close;
658 else if (!strncasecmp (p, "xref", 4))
660 p += 4;
661 strcat (s, "See ");
662 c = strlen (s);
663 goto close;
665 else if (!strncasecmp (p, "*", 1))
667 s[c++] = '\n';
668 s[c] = 0;
669 continue;
672 while (*p && *p != '{')
674 if (*++p == '*')
676 p++;
677 goto append;
681 close:
682 if (*p)
684 p++;
685 s[c++] = '`';
686 while (*p && *p != '}')
687 s[c++] = *p++;
689 if (*p)
690 p++;
692 s[c++] = '\'';
693 s[c] = 0;
696 else if (*p == '@' && *(p + 1) == '@')
697 p++;
699 append:
700 if (*p)
701 s[c++] = *p;
703 s[c] = 0;
706 s[c] = 0;
707 return s;
711 char *
712 strip_texi_and_wrap (const char *str, int html)
714 char *tmp = strip_texi (str, html);
715 char *help = xcalloc (1, strlen (tmp) * 2 + 1);
716 char *p, *ph;
717 int i;
719 for (p = tmp, ph = help, i = 0; *p; p++, i++)
721 if (i == 78 || *p == '\n')
723 if (!isspace (*p))
725 char *t = ph;
726 int n = 0;
728 while (*(--t) != ' ')
729 n++;
731 *t++ = '\n';
732 i = -1;
733 p -= n;
734 while (n--)
736 *t++ = *p++;
737 i++;
740 else
742 *ph++ = '\n';
743 i = -1;
744 while (*p != '\n' && *p == ' ')
745 p++;
749 if (*p == '\n')
750 continue;
751 *ph++ = *p;
754 *ph = 0;
755 xfree (tmp);
756 return help;
759 void
760 free_key (void *data)
762 xfree (data);
765 gpg_error_t
766 create_thread (void *(*cb) (void *), void *data,
767 pthread_t * tid, int detached)
769 pthread_attr_t attr;
770 int n;
772 pthread_attr_init (&attr);
774 if (detached)
775 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
777 n = pthread_create (tid, &attr, cb, data);
778 pthread_attr_destroy (&attr);
779 return gpg_error_from_errno (n);
782 void
783 release_mutex_cb (void *arg)
785 pthread_mutex_t *m = (pthread_mutex_t *) arg;
787 MUTEX_UNLOCK (m);
790 void
791 close_fd_cb (void *arg)
793 int fd = *(int *)arg;
795 if (fd != -1)
796 close (fd);
799 gpg_error_t
800 get_checksum_memory (void *data, size_t size, unsigned char **r_crc,
801 size_t *r_crclen)
803 size_t len = gcry_md_get_algo_dlen (GCRY_MD_CRC32);
804 unsigned char *crc = xmalloc (len);
806 *r_crc = NULL;
807 *r_crclen = 0;
809 if (!crc)
810 return GPG_ERR_ENOMEM;
812 gcry_md_hash_buffer (GCRY_MD_CRC32, crc, data, size);
813 *r_crc = crc;
814 *r_crclen = len;
815 return 0;
818 /* The advisory lock should be obtained before calling this function. */
819 gpg_error_t
820 get_checksum (const char *filename, unsigned char **r_crc, size_t * r_crclen)
822 int fd = -1;
823 unsigned char *buf;
824 gpg_error_t rc;
825 struct stat st;
827 rc = open_check_file (filename, &fd, &st, 1);
828 if (rc)
829 return rc;
831 pthread_cleanup_push (close_fd_cb, &fd);
832 buf = xmalloc (st.st_size);
833 if (buf)
835 pthread_cleanup_push (xfree, buf);
837 size_t len = read (fd, buf, st.st_size);
838 if (len == st.st_size)
840 if (buf)
841 rc = get_checksum_memory (buf, st.st_size, r_crc, r_crclen);
842 else
843 rc = GPG_ERR_ENOMEM;
845 else
846 rc = GPG_ERR_TOO_SHORT;
848 pthread_cleanup_pop (1);
850 else
851 rc = GPG_ERR_ENOMEM;
853 pthread_cleanup_pop (1);
854 return rc;
857 wchar_t *str_to_wchar (const char *str)
859 wchar_t *wc;
860 mbstate_t ps;
861 const char *p = str;
862 size_t len;
864 memset (&ps, 0, sizeof(mbstate_t));
865 len = mbsrtowcs (NULL, &p, 0, &ps);
866 if (len == -1)
867 return NULL;
869 wc = xcalloc (len+1, sizeof(wchar_t));
870 if (!wc)
871 return NULL;
873 p = str;
874 memset (&ps, 0, sizeof(mbstate_t));
875 len = mbsrtowcs (wc, &p, len, &ps);
876 if (len == -1)
878 xfree (wc);
879 return NULL;
882 return wc;
885 char *
886 gnupg_escape (const char *str)
888 if (!str || !*str)
889 return NULL;
891 char *buf = xmalloc (strlen(str)*4+1), *b = buf;
892 const char *p;
894 if (!buf)
895 return NULL;
897 for (p = str; p && *p; p++)
899 if (*p == ':')
901 *b++ = '\\';
902 *b++ = 'x';
903 *b++ = '3';
904 *b++ = 'a';
905 continue;
907 else if (*p == '\\')
909 *b++ = '\\';
910 *b++ = 'x';
911 *b++ = '5';
912 *b++ = 'c';
913 continue;
916 *b++ = *p;
919 *b = 0;
920 return buf;
923 /* From Beej's Guide to Network Programming. It's a good tutorial. */
924 void *
925 get_in_addr (struct sockaddr *sa)
927 if (sa->sa_family == AF_INET)
928 return &(((struct sockaddr_in *) sa)->sin_addr);
930 return &(((struct sockaddr_in6 *) sa)->sin6_addr);
933 gpg_error_t
934 open_check_file (const char *filename, int *r_fd, struct stat *r_st, int reg)
936 int fd;
937 gpg_error_t rc = 0;
939 if (r_st)
940 memset (r_st, 9, sizeof (struct stat));
942 if (reg)
944 struct stat st;
946 if (lstat (filename, &st) == -1)
947 return gpg_err_code (gpg_error_from_syserror ());
949 if (!S_ISREG (st.st_mode))
950 return GPG_ERR_ENODEV;
952 if (r_st)
953 memcpy (r_st, &st, sizeof (struct stat));
956 if (r_fd)
958 fd = open (filename, O_RDONLY);
959 if (fd == -1)
960 return gpg_err_code (gpg_error_from_syserror ());
962 if (r_st && fstat (fd, r_st) == -1)
964 rc = gpg_err_code (gpg_error_from_syserror ());
965 close (fd);
966 return rc;
969 *r_fd = fd;
972 return 0;