"Fossies" - the Fresh Open Source Software Archive

Member "msmtp-1.8.5/src/tools.c" (11 Jan 2019, 17193 Bytes) of package /linux/privat/msmtp-1.8.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "tools.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.8.1_vs_1.8.2.

    1 /*
    2  * tools.c
    3  *
    4  * This file is part of msmtp, an SMTP client, and of mpop, a POP3 client.
    5  *
    6  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2011, 2014, 2018, 2019
    7  * Martin Lambers <marlam@marlam.de>
    8  *
    9  *   This program is free software; you can redistribute it and/or modify
   10  *   it under the terms of the GNU General Public License as published by
   11  *   the Free Software Foundation; either version 3 of the License, or
   12  *   (at your option) any later version.
   13  *
   14  *   This program is distributed in the hope that it will be useful,
   15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17  *   GNU General Public License for more details.
   18  *
   19  *   You should have received a copy of the GNU General Public License
   20  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   21  */
   22 
   23 #ifdef HAVE_CONFIG_H
   24 # include "config.h"
   25 #endif
   26 
   27 #ifdef W32_NATIVE
   28 # define WIN32_LEAN_AND_MEAN    /* do not include more than necessary */
   29 # define _WIN32_WINNT 0x0601    /* Windows 7 or later */
   30 # include <windows.h>
   31 # include <io.h>
   32 # include <conio.h>
   33 # include <lmcons.h>
   34 # include <sys/locking.h>
   35 # include <limits.h>
   36 #else
   37 # include <pwd.h>
   38 #endif
   39 #include <stdio.h>
   40 #include <stdlib.h>
   41 #include <ctype.h>
   42 #include <string.h>
   43 #include <strings.h>
   44 #include <errno.h>
   45 #include <fcntl.h>
   46 #include <sys/types.h>
   47 #include <sys/stat.h>
   48 #include <time.h>
   49 #include <unistd.h>
   50 
   51 #include "xalloc.h"
   52 #include "tools.h"
   53 
   54 
   55 /*
   56  * exitcode_to_string()
   57  *
   58  * see tools.h
   59  */
   60 
   61 const char *exitcode_to_string(int exitcode)
   62 {
   63     switch (exitcode)
   64     {
   65         case EX_OK:
   66             return "EX_OK";
   67         case EX_USAGE:
   68             return "EX_USAGE";
   69         case EX_DATAERR:
   70             return "EX_DATAERR";
   71         case EX_NOINPUT:
   72             return "EX_NOINPUT";
   73         case EX_NOUSER:
   74             return "EX_NOUSER";
   75         case EX_NOHOST:
   76             return "EX_NOHOST";
   77         case EX_UNAVAILABLE:
   78             return "EX_UNAVAILABLE";
   79         case EX_SOFTWARE:
   80             return "EX_SOFTWARE";
   81         case EX_OSERR:
   82             return "EX_OSERR";
   83         case EX_OSFILE:
   84             return "EX_OSFILE";
   85         case EX_CANTCREAT:
   86             return "EX_CANTCREAT";
   87         case EX_IOERR:
   88             return "EX_IOERR";
   89         case EX_TEMPFAIL:
   90             return "EX_TEMPFAIL";
   91         case EX_PROTOCOL:
   92             return "EX_PROTOCOL";
   93         case EX_NOPERM:
   94             return "EX_NOPERM";
   95         case EX_CONFIG:
   96             return "EX_CONFIG";
   97         default:
   98             return "BUG:UNKNOWN";
   99     }
  100 }
  101 
  102 
  103 /*
  104  * link()
  105  *
  106  * A link replacement, currently only for W32.
  107  */
  108 #ifndef HAVE_LINK
  109 # if W32_NATIVE
  110 int link(const char *path1, const char *path2)
  111 {
  112     if (CreateHardLink(path2, path1, NULL) == 0)
  113     {
  114         /* It is not documented which errors CreateHardLink() can produce.
  115          * The following conversions are based on tests on a Windows XP SP2
  116          * system. */
  117         DWORD err = GetLastError();
  118         switch (err)
  119         {
  120             case ERROR_ACCESS_DENIED:
  121                 errno = EACCES;
  122                 break;
  123 
  124             case ERROR_INVALID_FUNCTION:        /* fs does not support hard links */
  125                 errno = EPERM;
  126                 break;
  127 
  128             case ERROR_NOT_SAME_DEVICE:
  129                 errno = EXDEV;
  130                 break;
  131 
  132             case ERROR_PATH_NOT_FOUND:
  133             case ERROR_FILE_NOT_FOUND:
  134                 errno = ENOENT;
  135                 break;
  136 
  137             case ERROR_INVALID_PARAMETER:
  138                 errno = ENAMETOOLONG;
  139                 break;
  140 
  141             case ERROR_TOO_MANY_LINKS:
  142                 errno = EMLINK;
  143                 break;
  144 
  145             case ERROR_ALREADY_EXISTS:
  146                 errno = EEXIST;
  147                 break;
  148 
  149             default:
  150                 errno = EIO;
  151         }
  152         return -1;
  153     }
  154 
  155     return 0;
  156 }
  157 # endif
  158 #endif
  159 
  160 /*
  161  * getpass()
  162  *
  163  * A getpass replacement, currently only for W32.
  164  * Taken from gnulib on 2011-03-20.
  165  * Original copyright:
  166  * Windows implementation by Martin Lambers <marlam@marlam.de>,
  167  * improved by Simon Josefsson.
  168  */
  169 #ifndef HAVE_GETPASS
  170 # if W32_NATIVE
  171 char *getpass(const char *prompt)
  172 {
  173     const size_t pass_max = 512;
  174     char getpassbuf[pass_max + 1];
  175     size_t i = 0;
  176     int c;
  177 
  178     if (prompt)
  179     {
  180         fputs(prompt, stderr);
  181         fflush(stderr);
  182     }
  183 
  184     for (;;)
  185     {
  186         c = _getch ();
  187         if (c == '\r')
  188         {
  189             getpassbuf[i] = '\0';
  190             break;
  191         }
  192         else if (i < pass_max)
  193         {
  194             getpassbuf[i++] = c;
  195         }
  196 
  197         if (i >= pass_max)
  198         {
  199             getpassbuf[i] = '\0';
  200             break;
  201         }
  202     }
  203 
  204     if (prompt)
  205     {
  206         fputs ("\r\n", stderr);
  207         fflush (stderr);
  208     }
  209 
  210     return strdup(getpassbuf);
  211 }
  212 # endif
  213 #endif
  214 
  215 
  216 /*
  217  * get_prgname()
  218  *
  219  * see tools.h
  220  */
  221 
  222 const char *get_prgname(const char *argv0)
  223 {
  224     const char *prgname;
  225 
  226     if (argv0)
  227     {
  228         prgname = strrchr(argv0, PATH_SEP);
  229         if (!prgname)
  230         {
  231             prgname = argv0;
  232         }
  233         else
  234         {
  235             prgname++;
  236         }
  237     }
  238     else
  239     {
  240         prgname = "";
  241     }
  242 
  243     return prgname;
  244 }
  245 
  246 
  247 /*
  248  * get_sysconfdir()
  249  *
  250  * see tools.h
  251  */
  252 
  253 char *get_sysconfdir(void)
  254 {
  255 #ifdef W32_NATIVE
  256 
  257     BYTE sysconfdir[MAX_PATH + 1];
  258     HKEY hkey;
  259     DWORD len;
  260     DWORD type;
  261     long l;
  262 
  263     l = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  264             "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\"
  265             "Shell Folders", 0, KEY_READ, &hkey);
  266     if (l != ERROR_SUCCESS)
  267     {
  268         return xstrdup("C:");
  269     }
  270     len = MAX_PATH;
  271     l = RegQueryValueEx(hkey, "Common AppData", NULL, &type, sysconfdir, &len);
  272     if (l != ERROR_SUCCESS || len >= MAX_PATH)
  273     {
  274         if (l != ERROR_SUCCESS || len >= MAX_PATH)
  275         {
  276             return xstrdup("C:");
  277         }
  278     }
  279     RegCloseKey(hkey);
  280     return xstrdup((char *)sysconfdir);
  281 
  282 #else /* UNIX */
  283 
  284 #ifdef SYSCONFDIR
  285     return xstrdup(SYSCONFDIR);
  286 #else
  287     return xstrdup("/etc");
  288 #endif
  289 
  290 #endif
  291 }
  292 
  293 
  294 /*
  295  * get_username()
  296  *
  297  * see tools.h
  298  */
  299 
  300 char *get_username(void)
  301 {
  302     char *username;
  303 #ifdef W32_NATIVE
  304     DWORD size = UNLEN + 1;
  305     TCHAR buf[UNLEN + 1];
  306 #else /* UNIX */
  307     struct passwd *pw;
  308 #endif
  309 
  310     username = getenv("USER");
  311     if (username)
  312     {
  313         username = xstrdup(username);
  314     }
  315     else
  316     {
  317         username = getenv("LOGNAME");
  318         if (username)
  319         {
  320             username = xstrdup(username);
  321         }
  322         else
  323         {
  324 #ifdef W32_NATIVE
  325             if (GetUserName(buf, &size))
  326             {
  327                 username = xstrdup((char *)buf);
  328             }
  329             else
  330             {
  331                 /* last resort */
  332                 username = xstrdup("unknown");
  333             }
  334 #else /* UNIX */
  335             username = getlogin();
  336             if (username)
  337             {
  338                 username = xstrdup(username);
  339             }
  340             else
  341             {
  342                 pw = getpwuid(getuid());
  343                 if (pw && pw->pw_name)
  344                 {
  345                     username = xstrdup(pw->pw_name);
  346                 }
  347                 else
  348                 {
  349                     /* last resort */
  350                     username = xstrdup("unknown");
  351                 }
  352             }
  353 #endif
  354         }
  355     }
  356 
  357     return username;
  358 }
  359 
  360 char *get_userconfig(const char *userconfigfile)
  361 {
  362     char *homedir = get_homedir();
  363     char *path = get_filename(homedir, userconfigfile);
  364 
  365 #if !defined(W32_NATIVE)
  366     struct stat buf;
  367     char *xdg_home;
  368     char *newpath;
  369 
  370     //does not exist, thus check XDG_CONFIG_HOME/PACKAGE_NAME/config
  371     if (stat(path, &buf) != 0) {
  372         xdg_home = getenv("XDG_CONFIG_HOME");
  373         if (xdg_home) {
  374             xdg_home = xstrdup(xdg_home);
  375         } else {
  376             xdg_home = expand_tilde("~/.config");
  377         }
  378         newpath = get_filename(xdg_home, PACKAGE_NAME);
  379         free(xdg_home);
  380         xdg_home = get_filename(newpath, "config");
  381         free(newpath);
  382         newpath = xdg_home;
  383         //If this does not exist fallback
  384         if (stat(newpath, &buf) == 0) {
  385             free(path);
  386             path = newpath;
  387         } else {
  388             free(newpath);
  389         }
  390     }
  391 #endif
  392 
  393     free(homedir);
  394     return path;
  395 }
  396 
  397 
  398 /*
  399  * get_homedir()
  400  *
  401  * see tools.h
  402  */
  403 
  404 char *get_homedir(void)
  405 {
  406 #ifdef W32_NATIVE
  407 
  408     char *home;
  409     BYTE homebuf[MAX_PATH + 1];
  410     HKEY hkey;
  411     DWORD len;
  412     DWORD type;
  413     long l;
  414 
  415     if ((home = getenv("HOME")))
  416     {
  417         home = xstrdup(home);
  418     }
  419     else
  420     {
  421         home = NULL;
  422         l = RegOpenKeyEx(HKEY_CURRENT_USER,
  423                 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\"
  424                 "Shell Folders", 0, KEY_READ, &hkey);
  425         if (l == ERROR_SUCCESS)
  426         {
  427             len = MAX_PATH;
  428             l = RegQueryValueEx(hkey, "AppData", NULL, &type, homebuf, &len);
  429             if (l == ERROR_SUCCESS && len < MAX_PATH)
  430             {
  431                 RegCloseKey(hkey);
  432                 home = xstrdup((char *)homebuf);
  433             }
  434         }
  435         if (!home)
  436         {
  437             home = xstrdup("C:");
  438         }
  439     }
  440 
  441     return home;
  442 
  443 #else /* UNIX */
  444 
  445     char *home;
  446     struct passwd *pw;
  447 
  448     if ((home = getenv("HOME")))
  449     {
  450         home = xstrdup(home);
  451     }
  452     else
  453     {
  454         pw = getpwuid(getuid());
  455         if (pw && pw->pw_dir)
  456         {
  457             home = xstrdup(pw->pw_dir);
  458         }
  459         else
  460         {
  461             home = xstrdup("");
  462         }
  463     }
  464 
  465     return home;
  466 
  467 #endif
  468 }
  469 
  470 
  471 /*
  472  * get_filename()
  473  *
  474  * see tools.h
  475  */
  476 
  477 char *get_filename(const char *directory, const char *name)
  478 {
  479     char *path;
  480     size_t dirlen;
  481 
  482     dirlen = strlen(directory);
  483     path = xmalloc((dirlen + strlen(name) + 2) * sizeof(char));
  484     strcpy(path, directory);
  485     if (dirlen == 0 || path[dirlen - 1] != PATH_SEP)
  486     {
  487         path[dirlen++] = PATH_SEP;
  488     }
  489     strcpy(path + dirlen, name);
  490 
  491     return path;
  492 }
  493 
  494 
  495 /*
  496  * expand_tilde()
  497  *
  498  * see tools.h
  499  */
  500 
  501 char *expand_tilde(const char *filename)
  502 {
  503     char *new_filename;
  504     size_t homedirlen;
  505 
  506     if (filename[0] == '~')
  507     {
  508         new_filename = get_homedir();
  509         homedirlen = strlen(new_filename);
  510         new_filename = xrealloc(new_filename,
  511                 (homedirlen + strlen(filename)) * sizeof(char));
  512         strcpy(new_filename + homedirlen, filename + 1);
  513         return new_filename;
  514     }
  515     else
  516     {
  517         return xstrdup(filename);
  518     }
  519 }
  520 
  521 
  522 /*
  523  * check_secure()
  524  *
  525  * see tools.h
  526  */
  527 
  528 int check_secure(const char *pathname)
  529 {
  530 #if defined W32_NATIVE || defined __CYGWIN__
  531 
  532     return 0;
  533 
  534 #else /* UNIX */
  535 
  536     struct stat statbuf;
  537 
  538     if (stat(pathname, &statbuf) < 0)
  539     {
  540         return 3;
  541     }
  542 
  543     if (statbuf.st_uid != geteuid())
  544     {
  545         return 1;
  546     }
  547     if (statbuf.st_mode & (S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP
  548                 | S_IROTH | S_IWOTH | S_IXOTH))
  549     {
  550         return 2;
  551     }
  552 
  553     return 0;
  554 
  555 #endif /* UNIX */
  556 }
  557 
  558 
  559 /*
  560  * lock_file()
  561  *
  562  * see tools.h
  563  */
  564 
  565 /* Helper function that sleeps for the tenth of a second */
  566 static void sleep_tenth_second(void)
  567 {
  568 #ifdef W32_NATIVE
  569     Sleep(100);
  570 #else /* POSIX */
  571     struct timespec tenth_second = { 0, 100000000 };
  572     nanosleep(&tenth_second, NULL);
  573 #endif
  574 }
  575 
  576 int lock_file(FILE *f, int lock_type, int timeout)
  577 {
  578     int fd;
  579     int lock_success;
  580     int tenth_seconds;
  581 #ifndef W32_NATIVE
  582     struct flock lock;
  583 #endif /* not W32_NATIVE */
  584 
  585     fd = fileno(f);
  586 #ifndef W32_NATIVE
  587     lock.l_type = (lock_type == TOOLS_LOCK_WRITE) ? F_WRLCK : F_RDLCK;
  588     lock.l_whence = SEEK_SET;
  589     lock.l_start = 0;
  590     lock.l_len = 0;
  591 #endif /* not W32_NATIVE */
  592     tenth_seconds = 0;
  593     for (;;)
  594     {
  595         errno = 0;
  596 #ifdef W32_NATIVE
  597         lock_success = (_locking(fd, _LK_NBLCK, LONG_MAX) != -1);
  598 #else /* UNIX */
  599         lock_success = (fcntl(fd, F_SETLK, &lock) != -1);
  600 #endif
  601         if (lock_success || (errno != EACCES && errno != EAGAIN)
  602             || tenth_seconds / 10 >= timeout)
  603         {
  604             break;
  605         }
  606         else
  607         {
  608             sleep_tenth_second();
  609             tenth_seconds++;
  610         }
  611     }
  612     return (lock_success ? 0 : (tenth_seconds / 10 >= timeout ? 1 : 2));
  613 }
  614 
  615 
  616 /*
  617  * string_replace()
  618  *
  619  * see tools.h
  620  */
  621 
  622 char *string_replace(char *str, const char *s, const char *r)
  623 {
  624     char *p, *new_str;
  625     size_t next_pos = 0;
  626     size_t slen = strlen(s);
  627     size_t rlen = strlen(r);
  628 
  629     while ((p = strstr(str + next_pos, s)))
  630     {
  631         new_str = xmalloc((strlen(str) + rlen - 1) * sizeof(char));
  632         strncpy(new_str, str, (size_t)(p - str));
  633         strcpy(new_str + (size_t)(p - str), r);
  634         strcpy(new_str + (size_t)(p - str) + rlen,
  635                 str + (size_t)(p - str) + slen);
  636         next_pos = (size_t)(p - str) + rlen;
  637         free(str);
  638         str = new_str;
  639     }
  640     return str;
  641 }
  642 
  643 
  644 /*
  645  * sanitize_string()
  646  *
  647  * see tools.h
  648  */
  649 
  650 char *sanitize_string(char *str)
  651 {
  652     char *p = str;
  653 
  654     while (*p != '\0')
  655     {
  656         if (iscntrl((unsigned char)*p))
  657         {
  658             *p = '?';
  659         }
  660         p++;
  661     }
  662 
  663     return str;
  664 }
  665 
  666 
  667 /*
  668  * print_fingerprint()
  669  *
  670  * see tools.h
  671  */
  672 
  673 void print_fingerprint(char *s, const unsigned char *fingerprint, size_t len)
  674 {
  675     const char *hex = "0123456789ABCDEF";
  676     size_t i;
  677 
  678     for (i = 0; i < len; i++)
  679     {
  680         s[3 * i + 0] = hex[(fingerprint[i] & 0xf0) >> 4];
  681         s[3 * i + 1] = hex[fingerprint[i] & 0x0f];
  682         s[3 * i + 2] = (i < len - 1 ? ':' : '\0');
  683     }
  684 }
  685 
  686 
  687 /*
  688  * print_time_rfc2822()
  689  *
  690  * see tools.h
  691  */
  692 
  693 void print_time_rfc2822(time_t t, char rfc2822_timestamp[32])
  694 {
  695     /* Calculate a RFC 2822 timestamp. strftime() is unreliable for this because
  696      * it is locale dependant, and because the timezone offset conversion
  697      * specifier %z is not portable. */
  698     struct tm gmt, *lt;
  699     char tz_offset_sign;
  700     int tz_offset_hours;
  701     int tz_offset_minutes;
  702     const char *weekday[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
  703         "Sat" };
  704     const char *month[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
  705         "Aug", "Sep", "Oct", "Nov", "Dec" };
  706 
  707     /* copy the struct tm, because the subsequent call to localtime() will
  708      * overwrite it */
  709     gmt = *gmtime(&t);
  710     lt = localtime(&t);
  711     tz_offset_minutes = (lt->tm_hour - gmt.tm_hour) * 60
  712         + lt->tm_min - gmt.tm_min
  713         + (lt->tm_year - gmt.tm_year) * 24 * 60
  714         + (lt->tm_yday - gmt.tm_yday) * 24 * 60;
  715     if (tz_offset_minutes < 0)
  716     {
  717         tz_offset_sign = '-';
  718         tz_offset_minutes = -tz_offset_minutes;
  719     }
  720     else
  721     {
  722         tz_offset_sign = '+';
  723     }
  724     tz_offset_hours = tz_offset_minutes / 60;
  725     tz_offset_minutes %= 60;
  726     if (tz_offset_hours > 99)
  727     {
  728         /* Values equal to or larger than 24 are not meaningful, but we just
  729          * make sure that the value fits into two digits. If the system time is
  730          * broken, we cannot fix it. */
  731         tz_offset_hours = 99;
  732     }
  733     (void)snprintf(rfc2822_timestamp, 32,
  734             "%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d",
  735             weekday[lt->tm_wday], lt->tm_mday, month[lt->tm_mon],
  736             lt->tm_year + 1900, lt->tm_hour, lt->tm_min, lt->tm_sec,
  737             tz_offset_sign, tz_offset_hours, tz_offset_minutes);
  738 }
  739 
  740 
  741 /*
  742  * split_mail_address()
  743  *
  744  * see tools.h
  745  */
  746 
  747 void split_mail_address(const char *address, char **local_part, char **domain_part)
  748 {
  749     const char *p = strrchr(address, '@');
  750     if (p)
  751     {
  752         size_t local_part_len = p - address;
  753         size_t domain_part_len = strlen(p + 1);
  754         *local_part = xmalloc(local_part_len + 1);
  755         strncpy(*local_part, address, local_part_len);
  756         (*local_part)[local_part_len] = '\0';
  757         *domain_part = xmalloc(domain_part_len + 1);
  758         strcpy(*domain_part, p + 1);
  759     }
  760     else
  761     {
  762         size_t local_part_len = strlen(address);
  763         *local_part = xmalloc(local_part_len + 1);
  764         strcpy(*local_part, address);
  765         *domain_part = NULL;
  766     }
  767 }
  768 
  769 
  770 /*
  771  * check_hostname_matches_domain()
  772  *
  773  * see tools.h
  774  */
  775 
  776 int check_hostname_matches_domain(const char *hostname, const char *domain)
  777 {
  778     size_t hostname_len = strlen(hostname);
  779     size_t domain_len = strlen(domain);
  780 
  781     /* empty domain? */
  782     if (domain_len < 1)
  783         return 0;
  784 
  785     /* host name shorter than domain? */
  786     if (hostname_len < domain_len)
  787         return 0;
  788 
  789     /* if lengths match, then the strings must match */
  790     if (hostname_len == domain_len)
  791         return strcasecmp(hostname, domain) == 0 ? 1 : 0;
  792 
  793     /* if host name is longer, than it must be at least two longer because of
  794      * the '.' (e.g. hostname="a.example.com", domain="example.com") */
  795     if (hostname_len < domain_len + 2)
  796         return 0;
  797 
  798     /* host name is at least two longer than domain name:
  799      * check that domain matches and that we have a separating dot
  800      * (so that hostname="xxexample.com" does not match "example.com") */
  801     return (hostname[hostname_len - 1 - domain_len] == '.'
  802             && strcasecmp(hostname + (hostname_len - domain_len), domain) == 0) ? 1 : 0;
  803 }