Add substitutions %t, %j, and %J as path-safe variants of %T, %i, and %I.
authorDr. Thomas Orgis <thomas.orgis@uni-hamburg.de>
Thu, 27 Jul 2017 10:54:28 +0000 (12:54 +0200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 8 Jan 2018 02:34:17 +0000 (03:34 +0100)
Rationale: Using the existing substitutions in construction of paths
(dynamic shares, created on client connect) results in directory names with
colons and dots in them. Those can be hard to use when accessed from a
different share, as Windows does not allow : in paths and has some ideas about
dots.

Signed-off-by: Dr. Thomas Orgis <thomas.orgis@uni-hamburg.de>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
docs-xml/manpages/smb.conf.5.xml
lib/util/time.c
lib/util/time.h
source3/lib/substitute.c
source3/torture/torture.c

index 976f988629dcbb9fa14b874af223f4fdea4e72c5..1e84b6b9062a30c7f6139a644a22773a9f16ab78 100644 (file)
@@ -511,6 +511,13 @@ chmod 1770 /usr/local/samba/lib/usershares
                </listitem>
                </varlistentry>
 
+               <varlistentry>
+               <term>%J</term>
+               <listitem><para>the IP address of the client machine,
+                       colons/dots replaced by underscores.</para>
+               </listitem>
+               </varlistentry>
+
                <varlistentry>
                <term>%i</term>
                <listitem><para>the local IP address to which a client connected.</para>
@@ -519,11 +526,23 @@ chmod 1770 /usr/local/samba/lib/usershares
                </listitem>
                </varlistentry>
 
+               <varlistentry>
+               <term>%j</term>
+               <listitem><para>the local IP address to which a client connected,
+                       colons/dots replaced by underscores.</para>
+               </listitem>
+               </varlistentry>
+
                <varlistentry>
                <term>%T</term>
                <listitem><para>the current date and time.</para></listitem>
                </varlistentry>
 
+               <varlistentry>
+               <term>%t</term>
+               <listitem><para>the current date and time in a minimal format without colons (YYYYYmmdd_HHMMSS).</para></listitem>
+               </varlistentry>
+
                <varlistentry>
                <term>%D</term>
                <listitem><para>name of the domain or workgroup of the current user.</para></listitem>
index 8a4d93d4ac130134d3a0096459137c5c4ac02539..bd067f84e8e5e49eff434e2c3b1dbff4d0e68506 100644 (file)
@@ -367,6 +367,57 @@ char *current_timestring(TALLOC_CTX *ctx, bool hires)
        return timeval_string(ctx, &tv, hires);
 }
 
+/*
+ * Return date and time as a minimal string avoiding funny characters
+ * that may cause trouble in file names. We only use digits and
+ * underscore ... or a minus/hyphen if we got negative time.
+ */
+char *minimal_timeval_string(TALLOC_CTX *ctx, const struct timeval *tp, bool hires)
+{
+       time_t t;
+       struct tm *tm;
+
+       t = (time_t)tp->tv_sec;
+       tm = localtime(&t);
+       if (!tm) {
+               if (hires) {
+                       return talloc_asprintf(ctx, "%ld_%06ld",
+                                              (long)tp->tv_sec,
+                                              (long)tp->tv_usec);
+               } else {
+                       return talloc_asprintf(ctx, "%ld", (long)t);
+               }
+       } else {
+               if (hires) {
+                       return talloc_asprintf(ctx,
+                                              "%04d%02d%02d_%02d%02d%02d_%06ld",
+                                              tm->tm_year+1900,
+                                              tm->tm_mon+1,
+                                              tm->tm_mday,
+                                              tm->tm_hour,
+                                              tm->tm_min,
+                                              tm->tm_sec,
+                                              (long)tp->tv_usec);
+               } else {
+                       return talloc_asprintf(ctx,
+                                              "%04d%02d%02d_%02d%02d%02d",
+                                              tm->tm_year+1900,
+                                              tm->tm_mon+1,
+                                              tm->tm_mday,
+                                              tm->tm_hour,
+                                              tm->tm_min,
+                                              tm->tm_sec);
+               }
+       }
+}
+
+char *current_minimal_timestring(TALLOC_CTX *ctx, bool hires)
+{
+       struct timeval tv;
+
+       GetTimeOfDay(&tv);
+       return minimal_timeval_string(ctx, &tv, hires);
+}
 
 /**
 return a HTTP/1.0 time string
index 42d23865b826fa964b1019401ad0cf2db9b57af6..9b897eb4b6cef2ce3cfd117a4b9891debd12ba91 100644 (file)
@@ -138,6 +138,21 @@ char *timeval_string(TALLOC_CTX *ctx, const struct timeval *tp, bool hires);
 **/
 char *current_timestring(TALLOC_CTX *ctx, bool hires);
 
+/**
+ Return a date and time as a string (optionally with microseconds)
+
+ format is %Y%m%d_%H%M%S or %Y%m%d_%H%M%S_%us
+**/
+
+char *minimal_timeval_string(TALLOC_CTX *ctx, const struct timeval *tp, bool hires);
+
+/**
+ Return the current date and time as a string (optionally with microseconds)
+
+ format is %Y%m%d_%H%M%S or %Y%m%d_%H%M%S_%us
+**/
+char *current_minimal_timestring(TALLOC_CTX *ctx, bool hires);
+
 /**
 return a HTTP/1.0 time string
 **/
index bc34c31797406dafc8d89c8c708d188a8e067052..9fdc5ca1edc7290de4802c2430af75cc4962b409 100644 (file)
@@ -454,6 +454,20 @@ void standard_sub_basic(const char *smb_name, const char *domain_name,
        TALLOC_FREE( s );
 }
 
+/*
+ * Limit addresses to hexalpha charactes and underscore, safe for path
+ * components for Windows clients.
+ */
+static void make_address_pathsafe(char *addr)
+{
+       while(addr && *addr) {
+               if(!isxdigit(*addr)) {
+                       *addr = '_';
+               }
+               ++addr;
+       }
+}
+
 /****************************************************************************
  Do some standard substitutions in a string.
  This function will return a talloced string that has to be freed.
@@ -550,11 +564,25 @@ char *talloc_sub_basic(TALLOC_CTX *mem_ctx,
                                sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0");
                        break;
                }
+               case 'J' : {
+                       r = talloc_strdup(tmp_ctx,
+                               sub_peeraddr[0] ? sub_peeraddr : "0.0.0.0");
+                       make_address_pathsafe(r);
+                       a_string = realloc_string_sub(a_string, "%J", r);
+                       break;
+               }
                case 'i': 
                        a_string = realloc_string_sub(
                                a_string, "%i",
                                sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0");
                        break;
+               case 'j' : {
+                       r = talloc_strdup(tmp_ctx,
+                               sub_sockaddr[0] ? sub_sockaddr : "0.0.0.0");
+                       make_address_pathsafe(r);
+                       a_string = realloc_string_sub(a_string, "%j", r);
+                       break;
+               }
                case 'L' : 
                        if ( strncasecmp_m(p, "%LOGONSERVER%", strlen("%LOGONSERVER%")) == 0 ) {
                                break;
@@ -578,6 +606,10 @@ char *talloc_sub_basic(TALLOC_CTX *mem_ctx,
                case 'T' :
                        a_string = realloc_string_sub(a_string, "%T", current_timestring(tmp_ctx, False));
                        break;
+               case 't' :
+                       a_string = realloc_string_sub(a_string, "%t",
+                                                     current_minimal_timestring(tmp_ctx, False));
+                       break;
                case 'a' :
                        a_string = realloc_string_sub(a_string, "%a",
                                        get_remote_arch_str());
index 1a9cf5bfd6be305c6e5a2f1a136b9fbade0ad916..ae502f280feeafd13702167ec87aaaacd1fc6bf1 100644 (file)
@@ -42,6 +42,7 @@
 #include "../libcli/smb/smbXcli_base.h"
 #include "lib/util/sys_rw_data.h"
 #include "lib/util/base64.h"
+#include "lib/util/time.h"
 
 extern char *optarg;
 extern int optind;
@@ -9906,6 +9907,54 @@ static bool run_symlink_open_test(int dummy)
        return correct;
 }
 
+/*
+ * Only testing minimal time strings, as the others
+ * need (locale-dependent) guessing at what strftime does and
+ * even may differ in builds.
+ */
+static bool timesubst_test(void)
+{
+       TALLOC_CTX *ctx = NULL;
+       /* Sa 23. Dez 04:33:20 CET 2017 */
+       const struct timeval tv = { 1514000000, 123 };
+       const char* expect_minimal = "20171223_033320";
+       const char* expect_minus   = "20171223_033320_000123";
+       char *s;
+       char *env_tz, *orig_tz = NULL;
+       bool result = true;
+
+       ctx = talloc_new(NULL);
+
+       env_tz = getenv("TZ");
+       if(env_tz) {
+               orig_tz = talloc_strdup(ctx, env_tz);
+       }
+       setenv("TZ", "UTC", 1);
+
+       s = minimal_timeval_string(ctx, &tv, false);
+
+       if(!s || strcmp(s, expect_minimal)) {
+               printf("minimal_timeval_string(ctx, tv, false) returned [%s], expected "
+                      "[%s]\n", s ? s : "<nil>", expect_minimal);
+               result = false;
+       }
+       TALLOC_FREE(s);
+       s = minimal_timeval_string(ctx, &tv, true);
+       if(!s || strcmp(s, expect_minus)) {
+               printf("minimal_timeval_string(ctx, tv, true) returned [%s], expected "
+                      "[%s]\n", s ? s : "<nil>", expect_minus);
+               result = false;
+       }
+       TALLOC_FREE(s);
+
+       if(orig_tz) {
+               setenv("TZ", orig_tz, 1);
+       }
+
+       TALLOC_FREE(ctx);
+       return result;
+}
+
 static bool run_local_substitute(int dummy)
 {
        bool ok = true;
@@ -9918,6 +9967,10 @@ static bool run_local_substitute(int dummy)
        ok &= subst_test("%G", "", "", -1, 0, gidtoname(0));
        ok &= subst_test("%D%u", "u", "dom", -1, 0, "domu");
        ok &= subst_test("%i %I", "", "", -1, -1, "0.0.0.0 0.0.0.0");
+       ok &= subst_test("%j %J", "", "", -1, -1, "0_0_0_0 0_0_0_0");
+       /* Substitution depends on current time, so better test the underlying
+          formatting function. At least covers %t. */
+       ok &= timesubst_test();
 
        /* Different captialization rules in sub_basic... */