HELP: Fix escaping HTML entities.
[pwmd.git] / src / util-misc.c
blob5642d5e21e70a02b49322567347e78e7f9b40725
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
3 2016, 2017
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of pwmd.
8 Pwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Pwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <string.h>
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 void log_write (const char *fmt, ...);
46 int
47 valid_filename (const char *filename)
49 const char *p;
51 if (!filename || !*filename)
52 return 0;
54 if (filename[0] == '-' && filename[1] == 0)
55 return 0;
57 for (p = filename; *p; p++)
59 if (*p == '/' || isspace (*p))
60 return 0;
63 return 1;
66 /* If 'user' is null then lookup entry for 'uid'. Caller must free 'buf'. */
67 struct passwd *
68 get_pwd_struct (const char *user, uid_t uid, struct passwd *pw, char **buf,
69 gpg_error_t *rc)
71 struct passwd *result = NULL;
72 #ifdef HAVE_GETPWNAM_R
73 size_t len;
74 int err = 0;
76 len = sysconf (_SC_GETPW_R_SIZE_MAX);
77 if (len == -1)
78 len = 16384;
80 *buf = xmalloc (len);
81 if (!*buf)
82 return NULL;
84 if (user)
85 err = getpwnam_r (user, pw, *buf, len, &result);
86 else
87 err = getpwuid_r (uid, pw, *buf, len, &result);
89 if (!err && result)
90 return result;
92 xfree (*buf);
93 *buf = NULL;
94 errno = err;
95 *rc = gpg_error_from_errno (err);
96 return NULL;
97 #else
98 if (buf)
99 *buf = NULL;
101 errno = 0;
102 if (user)
103 result = getpwnam (user);
104 else
105 result = getpwuid (uid);
107 if (!result)
108 *rc = gpg_error_from_syserror ();
110 return result;
111 #endif
114 static char *
115 get_pwd_entry(uid_t uid, int which)
117 gpg_error_t rc;
118 char *buf;
119 struct passwd pw;
120 struct passwd *result = get_pwd_struct (NULL, uid, &pw, &buf, &rc);
121 char *value = NULL;
123 if (!result)
124 return NULL;
126 if (result)
128 if (!which)
129 value = str_dup (result->pw_dir);
130 else
131 value = str_dup (result->pw_name);
134 xfree (buf);
135 return value;
138 char *
139 get_username (uid_t uid)
141 return get_pwd_entry (uid, 1);
144 char *
145 get_home_dir ()
147 if (home_directory)
148 return home_directory;
150 home_directory = get_pwd_entry (getuid(), 0);
151 return home_directory;
154 char *
155 expand_homedir (char *str)
157 char *p = str;
159 if (*p++ == '~')
161 char *dir = get_pwd_entry(getuid(), 0);
162 char *s;
164 if (!dir)
165 return NULL;
167 s = str_asprintf ("%s%s", dir, p);
168 xfree (dir);
169 return s;
172 return str_dup (str);
175 /* The 'more' parameter lets the calling command process remaining
176 * non-option arguments. */
177 gpg_error_t
178 parse_options (char **line, struct argv_s * args[], void *data, int more)
180 char *p = *line;
181 gpg_error_t rc = 0;
183 for (; p && *p; p++)
185 while (*p == ' ')
186 p++;
188 if (!*p)
189 break;
191 if (*p == '-' && *(p + 1) == '-')
193 p += 2;
194 char opt[255] = { 0 }, value[255] =
197 char *tp;
198 unsigned len;
200 if (!*p || *p == ' ')
202 if (*p)
203 p++;
205 break;
208 for (tp = opt, len = 0; *p; p++, len++)
210 if (len + 1 == 255)
211 return GPG_ERR_LINE_TOO_LONG;
213 if (*p == ' ' || *p == '=')
215 *tp = 0;
217 if (*p == '=')
219 int inquote = 0;
221 p++;
223 for (tp = value, len = 0; *p; p++, len++)
225 if (len + 1 == 255)
226 return GPG_ERR_LINE_TOO_LONG;
228 if (*p == '\"')
230 inquote = !inquote;
231 continue;
233 else if (*p == ' ' && !inquote)
234 break;
236 *tp++ = *p;
239 *tp = 0;
242 break;
245 *tp++ = *p;
248 *tp = 0;
249 int match = 0;
251 for (int i = 0; args[i]; i++)
253 if (!strcmp (args[i]->opt, opt))
255 log_write2 ("param: name='%s' value='%s'", opt, value);
256 if (args[i]->type == OPTION_TYPE_NOARG && *value)
257 return GPG_ERR_SYNTAX;
258 else if (args[i]->type == OPTION_TYPE_ARG && !*value)
259 return GPG_ERR_SYNTAX;
261 if (args[i]->func)
263 rc = args[i]->func (data, value);
264 if (rc)
265 return rc;
268 match = 1;
269 break;
273 if (!match)
274 return GPG_ERR_UNKNOWN_OPTION;
276 if (!*p)
277 break;
279 continue;
282 if (*(p+1) && !more)
283 return GPG_ERR_SYNTAX;
285 break;
288 *line = p;
289 return rc;
292 char *
293 bin2hex (const unsigned char *data, size_t len)
295 size_t size = len * 2 + 1;
296 char buf[size];
297 size_t n;
299 buf[0] = 0;
301 for (n = 0; n < len; n++)
303 char c[3];
305 sprintf (c, "%02X", data[n]);
306 strcat (buf, c);
309 return str_dup (buf);
312 static char *
313 html_escape (const char *line, size_t len, int space)
315 struct string_s *string = string_new ("");
316 size_t n;
317 char *tmp;
319 if (!string)
320 return NULL;
322 for (n = 0; n < len; n++)
324 switch (line[n])
326 case '<':
327 string = string_append (string, "&lt;");
328 break;
329 case '>':
330 string = string_append (string, "&gt;");
331 break;
332 case ' ':
333 if (space)
335 string = string_append (string, "%20");
336 break;
338 default:
339 string = string_append_printf (string, "%c", line[n]);
340 break;
344 tmp = string->str;
345 string_free (string, 0);
346 return tmp;
349 static char *
350 strip_texi_html (const char *line)
352 size_t len;
353 const char *p;
354 char *tmp;
355 struct string_s *string = string_new ("<html><body>");
357 if (!string)
358 return NULL;
360 /* The command usage text. */
361 p = strchr (line, '\n');
362 if (p)
364 len = strlen (line)-strlen(p);
365 tmp = html_escape (line, len, 0);
366 string = string_append_printf (string, "<b>%s</b><p>", tmp);
367 xfree (tmp);
368 p++;
370 else
371 p = line;
373 for (; p && *p; p++)
375 int non_anchor = 1;
376 int var = 0;
377 int code = 0;
378 int emph = 0;
379 int cite = 0;
381 if (*p == '@' && *(p + 1) != '@')
383 p++;
385 if (!strncasecmp (p, "end ", 4))
387 p += 4;
388 if (!strncasecmp (p, "table", 5))
390 string = string_append (string, "</p>");
392 else if (!strncasecmp (p, "example", 7))
394 string = string_append (string, "</code>");
397 while (*p++ != '\n');
398 p--;
399 continue;
401 else if (!strncasecmp (p, "table ", 6))
403 while (*p++ != '\n');
404 p--;
405 continue;
407 else if (!strncasecmp (p, "example", 7))
409 string = string_append (string, "<code>");
410 while (*p++ != '\n');
411 p--;
412 continue;
414 else if (!strncasecmp (p, "sp ", 3))
416 while (*p++ != '\n');
417 p--;
418 string = string_append (string, "<p>");
419 continue;
421 else if (!strncasecmp (p, "item ", 5))
423 p += 5;
424 string = string_append (string, "<p><b>");
425 tmp = strchr (p, '\n');
426 len = strlen (p) - strlen (tmp);
427 while (len--)
429 if (*p == '<')
431 string = string_append (string, "&lt;");
432 p++;
434 else if (*p == '>')
436 string = string_append (string, "&gt;");
437 p++;
439 else
440 string = string_append_printf (string, "%c", *p++);
443 string = string_append (string, "</b><br/>");
444 continue;
446 else if (!strncasecmp (p, "pxref", 5)
447 || !strncasecmp (p, "npxref", 6))
449 int n = 0;
451 if (!strncasecmp (p, "npxref", 6))
452 n++;
454 p += n ? 7 : 6;
455 non_anchor = 0;
456 string = string_append_printf (string, "%s<a href=\"",
457 n ? "" : "see ");
458 goto close;
460 else if (!strncasecmp (p, "xref", 4))
462 p += 5;
463 non_anchor = 0;
464 string = string_append (string, "See <a href=\"");
465 goto close;
467 else if (!strncasecmp (p, "url", 3))
469 p += 4;
470 non_anchor = 0;
471 string = string_append (string, "<a href=\"");
472 goto close;
474 else if (!strncasecmp (p, "key", 3))
476 p += 4;
477 goto close;
479 else if (!strncasecmp (p, "file", 4))
481 p += 5;
482 var = 1;
483 string = string_append (string, "<var>");
484 goto close;
486 else if (!strncasecmp (p, "var", 3))
488 p += 4;
489 var = 1;
490 string = string_append (string, "<var>");
491 goto close;
493 else if (!strncasecmp (p, "option", 6))
495 p += 7;
496 var = 1;
497 string = string_append (string, "<var>");
498 goto close;
500 else if (!strncasecmp (p, "code", 4))
502 p += 5;
503 code = 1;
504 string = string_append (string, "<b>");
505 goto close;
507 else if (!strncasecmp (p, "emph", 4))
509 p += 5;
510 emph = 1;
511 string = string_append (string, "<em>");
512 goto close;
514 else if (!strncasecmp (p, "command", 7))
516 p += 8;
517 emph = 1;
518 string = string_append (string, "<em>");
519 goto close;
521 else if (!strncasecmp (p, "cite", 4))
523 p += 5;
524 cite = 1;
525 string = string_append (string, "<cite>");
526 goto close;
528 else if (!strncasecmp (p, "abbr", 4))
530 p += 5;
531 emph = 1;
532 string = string_append (string, "<em>");
533 goto close;
535 else if (!strncasecmp (p, "*", 1))
537 string = string_append (string, "<br/>");
538 continue;
541 while (*p && *p != '{')
543 if (*++p == '*')
545 p++;
546 goto append;
550 close:
551 if (*p)
553 char buf [255];
555 tmp = strchr (p, '}');
556 len = strlen (p) - strlen (tmp);
557 tmp = buf;
558 while (len--)
559 *tmp++ = *p++;
561 *tmp = 0;
563 if (*p)
564 p++;
566 if (!non_anchor)
568 tmp = html_escape (buf, strlen (buf), 1);
569 string = string_append_printf (string, "%s\">%s</a>", tmp, buf);
570 xfree (tmp);
572 else
573 string = string_append (string, buf);
575 if (var)
576 string = string_append (string, "</var>");
577 else if (code)
578 string = string_append (string, "</b>");
579 else if (emph)
580 string = string_append (string, "</em>");
581 else if (cite)
582 string = string_append (string, "</cite>");
585 else if (*p == '@' && *(p + 1) == '@')
586 p++;
588 append:
589 string = string_append_printf (string, "%c", *p);
591 if (!*p)
592 break;
595 string = string_append (string, "</body></html>");
596 tmp = string->str;
597 string_free (string, 0);
598 return tmp;
601 static char *
602 strip_texi (const char *str, int html)
604 const char *p;
605 char *s;
606 int c = 0;
608 if (html)
609 return strip_texi_html (str);
611 s = str_dup (str);
612 if (!s)
613 return NULL;
615 for (p = str; *p; p++)
617 if (*p == '@' && *(p + 1) != '@')
619 p++;
621 if (!strncasecmp (p, "table", 5)
622 || !strncasecmp (p, "end ", 4))
624 while (*p++ != '\n');
625 p--;
626 continue;
628 else if (!strncasecmp (p, "example", 7)
629 || !strncasecmp (p, "end ", 4))
631 while (*p++ != '\n');
632 p--;
633 continue;
635 else if (!strncasecmp (p, "sp ", 3))
637 while (*p++ != '\n');
638 p--;
639 continue;
641 else if (!strncasecmp (p, "item", 4))
643 p += 5;
644 while (*p && *p != '\n')
645 s[c++] = *p++;
647 s[c++] = '\n';
648 continue;
650 else if (!strncasecmp (p, "pxref", 5))
652 p += 5;
653 strcat (s, "see ");
654 c = strlen (s);
655 goto close;
657 else if (!strncasecmp (p, "xref", 4))
659 p += 4;
660 strcat (s, "See ");
661 c = strlen (s);
662 goto close;
664 else if (!strncasecmp (p, "*", 1))
666 s[c++] = '\n';
667 s[c] = 0;
668 continue;
671 while (*p && *p != '{')
673 if (*++p == '*')
675 p++;
676 goto append;
680 close:
681 if (*p)
683 p++;
684 s[c++] = '`';
685 while (*p && *p != '}')
686 s[c++] = *p++;
688 if (*p)
689 p++;
691 s[c++] = '\'';
692 s[c] = 0;
695 else if (*p == '@' && *(p + 1) == '@')
696 p++;
698 append:
699 if (*p)
700 s[c++] = *p;
702 s[c] = 0;
705 s[c] = 0;
706 return s;
710 char *
711 strip_texi_and_wrap (const char *str, int html)
713 char *tmp = strip_texi (str, html);
714 char *help = xcalloc (1, strlen (tmp) * 2 + 1);
715 char *p, *ph;
716 int i;
718 for (p = tmp, ph = help, i = 0; *p; p++, i++)
720 if (i == 78 || *p == '\n')
722 if (!isspace (*p))
724 char *t = ph;
725 int n = 0;
727 while (*(--t) != ' ')
728 n++;
730 *t++ = '\n';
731 i = -1;
732 p -= n;
733 while (n--)
735 *t++ = *p++;
736 i++;
739 else
741 *ph++ = '\n';
742 i = -1;
743 while (*p != '\n' && *p == ' ')
744 p++;
748 if (*p == '\n')
749 continue;
750 *ph++ = *p;
753 *ph = 0;
754 xfree (tmp);
755 return help;
758 void
759 free_key (void *data)
761 xfree (data);
764 gpg_error_t
765 create_thread (void *(*cb) (void *), void *data,
766 pthread_t * tid, int detached)
768 pthread_attr_t attr;
769 int n;
771 pthread_attr_init (&attr);
773 if (detached)
774 pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
776 n = pthread_create (tid, &attr, cb, data);
777 pthread_attr_destroy (&attr);
778 return gpg_error_from_errno (n);
781 void
782 release_mutex_cb (void *arg)
784 pthread_mutex_t *m = (pthread_mutex_t *) arg;
786 MUTEX_UNLOCK (m);
789 void
790 close_fd_cb (void *arg)
792 int fd = *(int *)arg;
794 if (fd != -1)
795 close (fd);
798 gpg_error_t
799 get_checksum_memory (void *data, size_t size, unsigned char **r_crc,
800 size_t *r_crclen)
802 size_t len = gcry_md_get_algo_dlen (GCRY_MD_CRC32);
803 unsigned char *crc = xmalloc (len);
805 *r_crc = NULL;
806 *r_crclen = 0;
808 if (!crc)
809 return GPG_ERR_ENOMEM;
811 gcry_md_hash_buffer (GCRY_MD_CRC32, crc, data, size);
812 *r_crc = crc;
813 *r_crclen = len;
814 return 0;
817 /* The advisory lock should be obtained before calling this function. */
818 gpg_error_t
819 get_checksum (const char *filename, unsigned char **r_crc, size_t * r_crclen)
821 int fd = -1;
822 unsigned char *buf;
823 gpg_error_t rc;
824 struct stat st;
826 rc = open_check_file (filename, &fd, &st, 1);
827 if (rc)
828 return rc;
830 pthread_cleanup_push (close_fd_cb, &fd);
831 buf = xmalloc (st.st_size);
832 if (buf)
834 pthread_cleanup_push (xfree, buf);
836 size_t len = read (fd, buf, st.st_size);
837 if (len == st.st_size)
839 if (buf)
840 rc = get_checksum_memory (buf, st.st_size, r_crc, r_crclen);
841 else
842 rc = GPG_ERR_ENOMEM;
844 else
845 rc = GPG_ERR_TOO_SHORT;
847 pthread_cleanup_pop (1);
849 else
850 rc = GPG_ERR_ENOMEM;
852 pthread_cleanup_pop (1);
853 return rc;
856 wchar_t *str_to_wchar (const char *str)
858 wchar_t *wc;
859 mbstate_t ps;
860 const char *p = str;
861 size_t len;
863 memset (&ps, 0, sizeof(mbstate_t));
864 len = mbsrtowcs (NULL, &p, 0, &ps);
865 if (len == -1)
866 return NULL;
868 wc = xcalloc (len+1, sizeof(wchar_t));
869 if (!wc)
870 return NULL;
872 p = str;
873 memset (&ps, 0, sizeof(mbstate_t));
874 len = mbsrtowcs (wc, &p, len, &ps);
875 if (len == -1)
877 xfree (wc);
878 return NULL;
881 return wc;
884 char *
885 gnupg_escape (const char *str)
887 if (!str || !*str)
888 return NULL;
890 char *buf = xmalloc (strlen(str)*4+1), *b = buf;
891 const char *p;
893 if (!buf)
894 return NULL;
896 for (p = str; p && *p; p++)
898 if (*p == ':')
900 *b++ = '\\';
901 *b++ = 'x';
902 *b++ = '3';
903 *b++ = 'a';
904 continue;
906 else if (*p == '\\')
908 *b++ = '\\';
909 *b++ = 'x';
910 *b++ = '5';
911 *b++ = 'c';
912 continue;
915 *b++ = *p;
918 *b = 0;
919 return buf;
922 /* From Beej's Guide to Network Programming. It's a good tutorial. */
923 void *
924 get_in_addr (struct sockaddr *sa)
926 if (sa->sa_family == AF_INET)
927 return &(((struct sockaddr_in *) sa)->sin_addr);
929 return &(((struct sockaddr_in6 *) sa)->sin6_addr);
932 gpg_error_t
933 open_check_file (const char *filename, int *r_fd, struct stat *r_st, int reg)
935 int fd;
936 gpg_error_t rc = 0;
938 if (reg)
940 struct stat st;
942 if (lstat (filename, &st) == -1)
943 return gpg_err_code (gpg_error_from_syserror ());
945 if (!S_ISREG (st.st_mode))
946 return GPG_ERR_ENODEV;
948 if (r_st)
949 memcpy (r_st, &st, sizeof (struct stat));
952 if (r_fd)
954 fd = open (filename, O_RDONLY);
955 if (fd == -1)
956 return gpg_err_code (gpg_error_from_syserror ());
958 if (r_st && fstat (fd, r_st) == -1)
960 rc = gpg_err_code (gpg_error_from_syserror ());
961 close (fd);
962 return rc;
965 *r_fd = fd;
968 return 0;