+ *dest = '\0';
+ va_end(ap);
+
+ return ret;
+}
+
+unsigned int clean_fname(char *name)
+{
+ char *limit = name - 1, *t = name, *f = name;
+ int anchored;
+
+ if (!name)
+ return 0;
+
+ if ((anchored = *f == '/') != 0)
+ *t++ = *f++;
+ while (*f) {
+ /* discard extra slashes */
+ if (*f == '/') {
+ f++;
+ continue;
+ }
+ if (*f == '.') {
+ /* discard "." dirs (but NOT a trailing '.'!) */
+ if (f[1] == '/') {
+ f += 2;
+ continue;
+ }
+ /* collapse ".." dirs */
+ if (f[1] == '.' && (f[2] == '/' || !f[2])) {
+ char *s = t - 1;
+ if (s == name && anchored) {
+ f += 2;
+ continue;
+ }
+ while (s > limit && *--s != '/') {}
+ if (s != t - 1 && (s < name || *s == '/')) {
+ t = s + 1;
+ f += 2;
+ continue;
+ }
+ *t++ = *f++;
+ *t++ = *f++;
+ limit = t;
+ }
+ }
+ while (*f && (*t++ = *f++) != '/') {}
+ }
+
+ if (t > name+anchored && t[-1] == '/')
+ t--;
+ if (t == name)
+ *t++ = '.';
+ *t = '\0';
+
+ return t - name;
+}
+
+/* Make path appear as if a chroot had occurred. This handles a leading
+ * "/" (either removing it or expanding it) and any leading or embedded
+ * ".." components that attempt to escape past the module's top dir.
+ *
+ * If dest is NULL, a buffer is allocated to hold the result. If dest is
+ * the same buffer as p (the path) OR if reldir is NULL, a leading slash
+ * is dropped instead of being expanded to be the module's top dir.
+ *
+ * If reldir is non-NULL (and non-empty), it is a sanitized directory that
+ * the path will be relative to, so allow as many '..'s at the beginning of
+ * the path as there are components in reldir. This is used for symbolic
+ * link targets. If reldir is non-null and the path began with "/", to be
+ * completely like a chroot we should add in depth levels of ".." at the
+ * beginning of the path, but that would blow the assumption that the path
+ * doesn't grow and it is not likely to end up being a valid symlink
+ * anyway, so just do the normal removal of the leading "/" instead.
+ *
+ * While we're at it, remove double slashes and "." components like
+ * clean_fname() does, but DON'T remove a trailing slash because that is
+ * sometimes significant on command line arguments.
+ *
+ * If the resulting path would be empty, change it into ".".
+ */
+char *sanitize_path(char *dest, const char *p, const char *reldir)
+{
+ char *start, *sanp;
+ int depth = 0;
+ int allowdotdot = 0;
+ int rlen = 0;
+
+ if (dest != p) {
+ int plen = strlen(p);
+ if (*p == '/' && reldir) {
+ rlen = strlen(lp_path(module_id));
+ reldir = NULL;
+ p++;
+ }
+ if (dest) {
+ if (rlen + plen + 1 >= MAXPATHLEN)
+ return NULL;
+ } else if (!(dest = new_array(char, rlen + plen + 1)))
+ out_of_memory("sanitize_path");
+ if (rlen) {
+ memcpy(dest, lp_path(module_id), rlen);
+ if (rlen > 1)
+ dest[rlen++] = '/';
+ }
+ }
+
+ if (reldir) {
+ int new_component = 1;
+ while (*reldir) {
+ if (*reldir++ == '/')
+ new_component = 1;
+ else if (new_component) {
+ new_component = 0;
+ depth++;
+ }
+ }
+ }
+
+ start = sanp = dest + rlen;
+ while (*p == '/') {
+ /* remove leading slashes */
+ p++;
+ }
+ while (*p != '\0') {
+ /* this loop iterates once per filename component in p.
+ * both p (and sanp if the original had a slash) should
+ * always be left pointing after a slash
+ */
+ if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
+ /* skip "." component */
+ while (*++p == '/') {
+ /* skip following slashes */
+ ;
+ }
+ continue;
+ }
+ allowdotdot = 0;
+ if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ /* ".." component followed by slash or end */
+ if (depth > 0 && sanp == start) {
+ /* allow depth levels of .. at the beginning */
+ --depth;
+ allowdotdot = 1;
+ } else {
+ p += 2;
+ if (*p == '/')
+ p++;
+ if (sanp != start) {
+ /* back up sanp one level */
+ --sanp; /* now pointing at slash */
+ while (sanp > start && sanp[-1] != '/') {
+ /* skip back up to slash */
+ sanp--;
+ }
+ }
+ continue;
+ }
+ }
+ while (1) {
+ /* copy one component through next slash */
+ *sanp++ = *p++;
+ if (*p == '\0' || p[-1] == '/') {
+ while (*p == '/') {
+ /* skip multiple slashes */
+ p++;
+ }
+ break;
+ }
+ }
+ if (allowdotdot) {
+ /* move the virtual beginning to leave the .. alone */
+ start = sanp;
+ }
+ }
+ if (sanp == dest) {
+ /* ended up with nothing, so put in "." component */
+ *sanp++ = '.';
+ }
+ *sanp = '\0';
+
+ return dest;
+}
+
+char curr_dir[MAXPATHLEN];
+unsigned int curr_dir_len;
+
+/**
+ * Like chdir(), but it keeps track of the current directory (in the
+ * global "curr_dir"), and ensures that the path size doesn't overflow.
+ * Also cleans the path using the clean_fname() function.
+ **/
+int push_dir(char *dir)
+{
+ static int initialised;
+ unsigned int len;
+
+ if (!initialised) {
+ initialised = 1;
+ getcwd(curr_dir, sizeof curr_dir - 1);
+ curr_dir_len = strlen(curr_dir);
+ }
+
+ if (!dir) /* this call was probably just to initialize */
+ return 0;
+
+ len = strlen(dir);
+ if (len == 1 && *dir == '.')
+ return 1;
+
+ if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir)
+ return 0;
+
+ if (chdir(dir))
+ return 0;
+
+ if (*dir == '/') {
+ memcpy(curr_dir, dir, len + 1);
+ curr_dir_len = len;
+ } else {
+ curr_dir[curr_dir_len++] = '/';
+ memcpy(curr_dir + curr_dir_len, dir, len + 1);
+ curr_dir_len += len;
+ }
+
+ curr_dir_len = clean_fname(curr_dir);
+
+ return 1;
+}
+
+/**
+ * Reverse a push_dir() call. You must pass in an absolute path
+ * that was copied from a prior value of "curr_dir".
+ **/
+int pop_dir(char *dir)
+{
+ if (chdir(dir))
+ return 0;
+
+ curr_dir_len = strlcpy(curr_dir, dir, sizeof curr_dir);
+ if (curr_dir_len >= sizeof curr_dir)
+ curr_dir_len = sizeof curr_dir - 1;
+
+ return 1;
+}
+
+/**
+ * Return the filename, turning any newlines into '?'s. This ensures that
+ * outputting it on a line of its own cannot generate an empty line. This
+ * function can handle only 2 names at a time!
+ **/
+const char *safe_fname(const char *fname)
+{
+ static char fbuf1[MAXPATHLEN], fbuf2[MAXPATHLEN];
+ static char *fbuf = fbuf2;
+ char *nl = strchr(fname, '\n');
+
+ if (!nl)
+ return fname;
+
+ fbuf = fbuf == fbuf1 ? fbuf2 : fbuf1;
+ strlcpy(fbuf, fname, MAXPATHLEN);
+ nl = fbuf + (nl - (char *)fname);
+ do {
+ *nl = '?';
+ } while ((nl = strchr(nl+1, '\n')) != NULL);
+
+ return fbuf;
+}
+
+/**
+ * Return a quoted string with the full pathname of the indicated filename.
+ * The string " (in MODNAME)" may also be appended. The returned pointer
+ * remains valid until the next time full_fname() is called.
+ **/
+char *full_fname(const char *fn)
+{
+ static char *result = NULL;
+ char *m1, *m2, *m3;
+ char *p1, *p2;
+
+ if (result)
+ free(result);
+
+ fn = safe_fname(fn);
+ if (*fn == '/')
+ p1 = p2 = "";
+ else {
+ p1 = curr_dir;
+ p2 = "/";
+ }
+ if (module_id >= 0) {
+ m1 = " (in ";
+ m2 = lp_name(module_id);
+ m3 = ")";
+ if (*p1) {
+ if (!lp_use_chroot(module_id)) {
+ char *p = lp_path(module_id);
+ if (*p != '/' || p[1])
+ p1 += strlen(p);
+ }
+ if (!*p1)
+ p2++;
+ else
+ p1++;
+ }
+ else
+ fn++;
+ } else
+ m1 = m2 = m3 = "";
+
+ asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3);
+
+ return result;
+}
+
+static char partial_fname[MAXPATHLEN];
+
+char *partial_dir_fname(const char *fname)
+{
+ char *t = partial_fname;
+ int sz = sizeof partial_fname;
+ const char *fn;
+
+ if ((fn = strrchr(fname, '/')) != NULL) {
+ fn++;
+ if (*partial_dir != '/') {
+ int len = fn - fname;
+ strncpy(t, fname, len); /* safe */
+ t += len;
+ sz -= len;
+ }
+ } else
+ fn = fname;
+ if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
+ return NULL;
+ if (server_exclude_list.head
+ && check_exclude(&server_exclude_list, partial_fname, 0) < 0)
+ return NULL;
+
+ return partial_fname;
+}
+
+/* If no --partial-dir option was specified, we don't need to do anything
+ * (the partial-dir is essentially '.'), so just return success. */
+int handle_partial_dir(const char *fname, int create)
+{
+ char *fn, *dir;
+
+ if (fname != partial_fname)
+ return 1;
+ if (!create && *partial_dir == '/')
+ return 1;
+ if (!(fn = strrchr(partial_fname, '/')))
+ return 1;
+
+ *fn = '\0';
+ dir = partial_fname;
+ if (create) {
+ STRUCT_STAT st;
+#if SUPPORT_LINKS
+ int statret = do_lstat(dir, &st);
+#else
+ int statret = do_stat(dir, &st);
+#endif
+ if (statret == 0 && !S_ISDIR(st.st_mode)) {
+ if (do_unlink(dir) < 0)
+ return 0;
+ statret = -1;
+ }
+ if (statret < 0 && do_mkdir(dir, 0700) < 0)
+ return 0;
+ } else
+ do_rmdir(dir);
+ *fn = '/';
+
+ return 1;
+}
+
+/** We need to supply our own strcmp function for file list comparisons
+ to ensure that signed/unsigned usage is consistent between machines. */
+int u_strcmp(const char *cs1, const char *cs2)
+{
+ const uchar *s1 = (const uchar *)cs1;
+ const uchar *s2 = (const uchar *)cs2;
+
+ while (*s1 && *s2 && (*s1 == *s2)) {
+ s1++; s2++;
+ }
+
+ return (int)*s1 - (int)*s2;
+}
+
+
+
+/**
+ * Determine if a symlink points outside the current directory tree.
+ * This is considered "unsafe" because e.g. when mirroring somebody
+ * else's machine it might allow them to establish a symlink to
+ * /etc/passwd, and then read it through a web server.
+ *
+ * Null symlinks and absolute symlinks are always unsafe.
+ *
+ * Basically here we are concerned with symlinks whose target contains
+ * "..", because this might cause us to walk back up out of the
+ * transferred directory. We are not allowed to go back up and
+ * reenter.
+ *
+ * @param dest Target of the symlink in question.
+ *
+ * @param src Top source directory currently applicable. Basically this
+ * is the first parameter to rsync in a simple invocation, but it's
+ * modified by flist.c in slightly complex ways.
+ *
+ * @retval True if unsafe
+ * @retval False is unsafe
+ *
+ * @sa t_unsafe.c
+ **/
+int unsafe_symlink(const char *dest, const char *src)
+{
+ const char *name, *slash;
+ int depth = 0;
+
+ /* all absolute and null symlinks are unsafe */
+ if (!dest || !*dest || *dest == '/')
+ return 1;
+
+ /* find out what our safety margin is */
+ for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ if (strncmp(name, "../", 3) == 0) {
+ depth = 0;
+ } else if (strncmp(name, "./", 2) == 0) {
+ /* nothing */
+ } else {
+ depth++;
+ }
+ }
+ if (strcmp(name, "..") == 0)
+ depth = 0;
+
+ for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
+ if (strncmp(name, "../", 3) == 0) {
+ /* if at any point we go outside the current directory
+ then stop - it is unsafe */
+ if (--depth < 0)
+ return 1;
+ } else if (strncmp(name, "./", 2) == 0) {
+ /* nothing */
+ } else {
+ depth++;
+ }
+ }
+ if (strcmp(name, "..") == 0)
+ depth--;
+
+ return (depth < 0);
+}
+
+
+/**
+ * Return the date and time as a string
+ **/
+char *timestring(time_t t)
+{
+ static char TimeBuf[200];
+ struct tm *tm = localtime(&t);
+
+#ifdef HAVE_STRFTIME
+ strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm);
+#else
+ strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf);
+#endif
+
+ if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
+ TimeBuf[strlen(TimeBuf)-1] = 0;
+ }
+
+ return(TimeBuf);
+}
+
+
+/**
+ * Sleep for a specified number of milliseconds.
+ *
+ * Always returns TRUE. (In the future it might return FALSE if
+ * interrupted.)
+ **/
+int msleep(int t)
+{
+ int tdiff = 0;
+ struct timeval tval, t1, t2;
+
+ gettimeofday(&t1, NULL);
+
+ while (tdiff < t) {
+ tval.tv_sec = (t-tdiff)/1000;
+ tval.tv_usec = 1000*((t-tdiff)%1000);
+
+ errno = 0;
+ select(0,NULL,NULL, NULL, &tval);
+
+ gettimeofday(&t2, NULL);
+ tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
+ (t2.tv_usec - t1.tv_usec)/1000;
+ }
+
+ return True;
+}
+
+
+/**
+ * Determine if two file modification times are equivalent (either
+ * exact or in the modification timestamp window established by
+ * --modify-window).
+ *
+ * @retval 0 if the times should be treated as the same
+ *
+ * @retval +1 if the first is later
+ *
+ * @retval -1 if the 2nd is later
+ **/
+int cmp_modtime(time_t file1, time_t file2)
+{
+ if (file2 > file1) {
+ if (file2 - file1 <= modify_window)
+ return 0;
+ return -1;
+ }
+ if (file1 - file2 <= modify_window)
+ return 0;
+ return 1;
+}
+
+
+#ifdef __INSURE__XX
+#include <dlfcn.h>
+
+/**
+ This routine is a trick to immediately catch errors when debugging
+ with insure. A xterm with a gdb is popped up when insure catches
+ a error. It is Linux specific.
+**/
+int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
+{
+ static int (*fn)();
+ int ret;
+ char *cmd;
+
+ asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'",
+ getpid(), getpid(), getpid());
+
+ if (!fn) {
+ static void *h;
+ h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
+ fn = dlsym(h, "_Insure_trap_error");
+ }
+
+ ret = fn(a1, a2, a3, a4, a5, a6);
+
+ system(cmd);
+
+ free(cmd);
+
+ return ret;
+}
+#endif
+
+
+#define MALLOC_MAX 0x40000000
+
+void *_new_array(unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ return malloc(size * num);
+}
+
+void *_realloc_array(void *ptr, unsigned int size, unsigned long num)
+{
+ if (num >= MALLOC_MAX/size)
+ return NULL;
+ /* No realloc should need this, but just in case... */
+ if (!ptr)
+ return malloc(size * num);
+ return realloc(ptr, size * num);