{"thread":{"id":"5367","subject":"[PATCH] git-daemon virtual hosting implementation.","startedAt":"2006-08-23T18:52:37Z","lastAt":"2006-08-27T16:26:45Z","messageCount":16,"participants":["Pierre Habouzit","Junio C Hamano","Jon Loeliger","Jakub Narebski","Randal L. Schwartz"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"25813","messageId":"11563591572194-git-send-email-madcoder@debian.org","threadId":"5367","inReplyTo":null,"subject":"[PATCH] git-daemon virtual hosting implementation.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-23T18:52:37Z","receivedAt":"2006-08-23T18:52:37Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"just add the hostname in the path when using --base-path and --user-path.\nthis should be enough for most needs.\n\nSigned-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n Here is a proposal for daemon side virtualhosting support.\n\n This is quite simple, and just uses the hostname in the path where it looks\n for the git repository. Only works in conjuction of --base-path or\n --user-path btw.\n\n Documentation/git-daemon.txt |   12 +++++++\n daemon.c                     |   73 +++++++++++++++++++++++++++++++++++-------\n 2 files changed, 73 insertions(+), 12 deletions(-)\n\ndiff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt\nindex 0f7d274..ab5b232 100644\n--- a/Documentation/git-daemon.txt\n+++ b/Documentation/git-daemon.txt\n@@ -79,6 +79,18 @@ OPTIONS\n \ttaken as a request to access `path/foo` repository in\n \tthe home directory of user `alice`.\n \n+--use-vhosts::\n+\tUse git daemon cheap virtual hosting scheme.\n++\n+Used with --base-path=/foo, it will search the repositories in /foo/HOSTNAME\n+instead of /foo - if you try to pull git://git.example.com/hello.git, it will\n+search into /foo/git.example.com/hello.git.\n++\n+When using --user-path the HOSTNAME is appended to the path as well, so for\n+--user-path=some/path, request to git://git.example.com/~alice/foo is taken as\n+a request to access some/path/git.example.com/foo in the home directory of\n+user `alice`.\n+\n --verbose::\n \tLog details about the incoming connections and requested files.\n \ndiff --git a/daemon.c b/daemon.c\nindex 012936f..146482c 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -17,7 +17,7 @@ static int reuseaddr;\n \n static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n-\"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n+\"           [--timeout=n] [--init-timeout=n] [--use-vhosts] [--strict-paths]\\n\"\n \"           [--base-path=path] [--user-path | --user-path=path]\\n\"\n \"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]\";\n \n@@ -25,6 +25,8 @@ static const char daemon_usage[] =\n static char **ok_paths;\n static int strict_paths;\n \n+static int use_vhosts;\n+\n /* If this is set, git-daemon-export-ok is not required */\n static int export_all_trees;\n \n@@ -148,7 +150,7 @@ static int avoid_alias(char *p)\n \t}\n }\n \n-static char *path_ok(char *dir)\n+static char *path_ok(char *dir, char *vhost)\n {\n \tstatic char rpath[PATH_MAX];\n \tchar *path;\n@@ -158,6 +160,11 @@ static char *path_ok(char *dir)\n \t\treturn NULL;\n \t}\n \n+\tif (use_vhosts && !vhost) {\n+\t\tlogerror(\"using virtual hosting, and not host= was specified !\");\n+\t\treturn NULL;\n+\t}\n+\n \tif (*dir == '~') {\n \t\tif (!user_path) {\n \t\t\tlogerror(\"'%s': User-path not allowed\", dir);\n@@ -174,9 +181,25 @@ static char *path_ok(char *dir)\n \t\t\t\tslash = dir + restlen;\n \t\t\tnamlen = slash - dir;\n \t\t\trestlen -= namlen;\n-\t\t\tloginfo(\"userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>\", user_path, dir, namlen, restlen, slash);\n-\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s%.*s\",\n-\t\t\t\t namlen, dir, user_path, restlen, slash);\n+\n+\t\t\tif (use_vhosts) {\n+\t\t\t\tloginfo(\"host <%s>, \"\n+\t\t\t\t\t\"userpath <%s>, request <%s>, \"\n+\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n+\t\t\t\t\tvhost,\n+\t\t\t\t\tuser_path, dir,\n+\t\t\t\t\tnamlen, restlen, slash);\n+\t\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s/%s%.*s\",\n+\t\t\t\t\t namlen, dir, user_path, vhost,\n+\t\t\t\t\t restlen, slash);\n+\t\t\t} else {\n+\t\t\t\tloginfo(\"userpath <%s>, request <%s>, \"\n+\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n+\t\t\t\t\tuser_path, dir,\n+\t\t\t\t\tnamlen, restlen, slash);\n+\t\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s%.*s\",\n+\t\t\t\t\t namlen, dir, user_path, restlen, slash);\n+\t\t\t}\n \t\t\tdir = rpath;\n \t\t}\n \t}\n@@ -187,7 +210,11 @@ static char *path_ok(char *dir)\n \t\t\treturn NULL;\n \t\t}\n \t\telse {\n-\t\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n+\t\t\tif (use_vhosts) {\n+\t\t\t\tsnprintf(rpath, PATH_MAX, \"%s/%s%s\", base_path, vhost, dir);\n+\t\t\t} else {\n+\t\t\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n+\t\t\t}\n \t\t\tdir = rpath;\n \t\t}\n \t}\n@@ -229,15 +256,19 @@ static char *path_ok(char *dir)\n \treturn NULL;\t\t/* Fallthrough. Deny by default */\n }\n \n-static int upload(char *dir)\n+static int upload(char *dir, char *vhost)\n {\n \t/* Timeout as string */\n \tchar timeout_buf[64];\n \tconst char *path;\n \n-\tloginfo(\"Request for '%s'\", dir);\n+\tif (vhost) {\n+\t\tloginfo(\"Request for '%s' (host = %s)\", dir, vhost);\n+\t} else {\n+\t\tloginfo(\"Request for '%s'\", dir);\n+\t}\n \n-\tif (!(path = path_ok(dir)))\n+\tif (!(path = path_ok(dir, vhost)))\n \t\treturn -1;\n \n \t/*\n@@ -274,6 +305,7 @@ static int execute(struct sockaddr *addr\n {\n \tstatic char line[1000];\n \tint pktlen, len;\n+        char *vhost = NULL;\n \n \tif (addr) {\n \t\tchar addrbuf[256] = \"\";\n@@ -303,15 +335,28 @@ #endif\n \talarm(0);\n \n \tlen = strlen(line);\n-\tif (pktlen != len)\n+\tif (pktlen != len) {\n+\t\tint arg_pos = len + 1;\n+\n \t\tloginfo(\"Extended attributes (%d bytes) exist <%.*s>\",\n \t\t\t(int) pktlen - len,\n-\t\t\t(int) pktlen - len, line + len + 1);\n+\t\t\t(int) pktlen - len, line + arg_pos);\n+\n+\t\twhile (arg_pos < pktlen) {\n+\t\t\tint arg_len = strlen(line + arg_pos);\n+\n+\t\t\tif (!strncmp(\"host=\", line + arg_pos, 5)) {\n+\t\t\t\tvhost = line + arg_pos + 5;\n+\t\t\t}\n+\n+\t\t\targ_pos += arg_len + 1;\n+\t\t}\n+\t}\n \tif (len && line[len-1] == '\\n')\n \t\tline[--len] = 0;\n \n \tif (!strncmp(\"git-upload-pack \", line, 16))\n-\t\treturn upload(line+16);\n+\t\treturn upload(line+16, vhost);\n \n \tlogerror(\"Protocol error: '%s'\", line);\n \treturn -1;\n@@ -762,6 +807,10 @@ int main(int argc, char **argv)\n \t\t\tinit_timeout = atoi(arg+15);\n \t\t\tcontinue;\n \t\t}\n+\t\tif (!strcmp(arg, \"--use-vhosts\")) {\n+\t\t\tuse_vhosts = 1;\n+\t\t\tcontinue;\n+\t\t}\n \t\tif (!strcmp(arg, \"--strict-paths\")) {\n \t\t\tstrict_paths = 1;\n \t\t\tcontinue;\n-- \n1.4.1.1\n"},{"id":"25817","messageId":"7vmz9vgqlm.fsf@assigned-by-dhcp.cox.net","threadId":"5367","inReplyTo":"11563591572194-git-send-email-madcoder@debian.org","subject":"Re: [PATCH] git-daemon virtual hosting implementation.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-23T20:11:01Z","receivedAt":"2006-08-23T20:11:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pierre Habouzit <madcoder@debian.org> writes:\n\n> just add the hostname in the path when using --base-path and --user-path.\n> this should be enough for most needs.\n>\n> Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> ---\n>  Here is a proposal for daemon side virtualhosting support.\n\n> @@ -158,6 +160,11 @@ static char *path_ok(char *dir)\n>  \t\treturn NULL;\n>  \t}\n>  \n> +\tif (use_vhosts && !vhost) {\n> +\t\tlogerror(\"using virtual hosting, and not host= was specified !\");\n> +\t\treturn NULL;\n> +\t}\n> +\n\nThis part is objectionable -- older clients do not give \"host=\".\nI think the plan, when virtual hosting was proposed and we added\nthis to the client side first, was to treat older clients as if\nthey specified the \"primary\" host.  So we would need some\nmechanism to say where the repositories of the \"primary\" host\nlives.\n\n> +\t\t\tif (use_vhosts) {\n> +\t\t\t\tloginfo(\"host <%s>, \"\n> +\t\t\t\t\t\"userpath <%s>, request <%s>, \"\n> +\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n> +\t\t\t\t\tvhost,\n> +\t\t\t\t\tuser_path, dir,\n> +\t\t\t\t\tnamlen, restlen, slash);\n> +\t\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s/%s%.*s\",\n> +\t\t\t\t\t namlen, dir, user_path, vhost,\n> +\t\t\t\t\t restlen, slash);\n\nI am not sure if the interaction between user-path and vhost\nshould go like this, but I do not think of a good alternative to\nsuggest right now.  Your code allows ~user/host1 and ~user/host2\nto host different set of repositories, but I suspect if somebody\nis setting up a virtual hosting of two hosts, he might want to\nhave two distinct set of users (iow to pretend that ~user exist\nonly on host1 but not on host2).  I dunno.\n"},{"id":"25818","messageId":"200608232256.10108.madcoder@debian.org","threadId":"5367","inReplyTo":"7vmz9vgqlm.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon virtual hosting implementation.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-23T20:56:07Z","receivedAt":"2006-08-23T20:56:07Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Le mer 23 août 2006 22:11, Junio C Hamano a écrit :\n> Pierre Habouzit <madcoder@debian.org> writes:\n> > just add the hostname in the path when using --base-path and\n> > --user-path. this should be enough for most needs.\n> >\n> > Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> > ---\n> >  Here is a proposal for daemon side virtualhosting support.\n> >\n> > @@ -158,6 +160,11 @@ static char *path_ok(char *dir)\n> >  \t\treturn NULL;\n> >  \t}\n> >\n> > +\tif (use_vhosts && !vhost) {\n> > +\t\tlogerror(\"using virtual hosting, and not host= was specified\n> > !\"); +\t\treturn NULL;\n> > +\t}\n> > +\n>\n> This part is objectionable -- older clients do not give \"host=\".\n> I think the plan, when virtual hosting was proposed and we added\n> this to the client side first, was to treat older clients as if\n> they specified the \"primary\" host.  So we would need some\n> mechanism to say where the repositories of the \"primary\" host\n> lives.\n\nfair enough, so I suppose there is many choices:\n * replace --use-vhosts with --use-vhosts=${default hostname}\n   but I do not like it very much\n * use `hostname -f` as the default hostname (not very nice either)\n * use the magic _default_ (à la apache) hostname ?\n\nBut just note that if a git repository is accessible on an host that is \nnot the default, and only on that one, an older client won't be able to \nclone the repository anyway, because he will be redirected to the \ndefault virtual host. Meaning, that someone that uses virtual hosts \nwill break older clients anyway.\n\nwhat would be nicer would be a way to gracefully reject older clients in \nthat case with an understandable error message, so that the user knows \nwhy it does not work. I don't know PROTO_GIT very well yet,so I don't \nknow if older client support such communications yet or not.\n\n> > +\t\t\tif (use_vhosts) {\n> > +\t\t\t\tloginfo(\"host <%s>, \"\n> > +\t\t\t\t\t\"userpath <%s>, request <%s>, \"\n> > +\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n> > +\t\t\t\t\tvhost,\n> > +\t\t\t\t\tuser_path, dir,\n> > +\t\t\t\t\tnamlen, restlen, slash);\n> > +\t\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s/%s%.*s\",\n> > +\t\t\t\t\t namlen, dir, user_path, vhost,\n> > +\t\t\t\t\t restlen, slash);\n>\n> I am not sure if the interaction between user-path and vhost\n> should go like this, but I do not think of a good alternative to\n> suggest right now.  Your code allows ~user/host1 and ~user/host2\n> to host different set of repositories, but I suspect if somebody\n> is setting up a virtual hosting of two hosts, he might want to\n> have two distinct set of users (iow to pretend that ~user exist\n> only on host1 but not on host2).  I dunno.\n\nAnother option would be not to support virtual hosts, but instead \nsuperseed the --base-path and --user-path with some --base-path-fmt \nand --user-path-fmt where the user can specify how to build the path \nwith simple sprintf-like formats. One could e.g. support:\n * %% obviously ;\n * %h that will be replaced with the hostname\n * %u (only for --user-path-fmt)\n * %p (asked path)\n * ...\n\nI think that's more clever, and allow more flexible use of the virtual \nhosting code. It e.g. allow to use the virtual host scheme for the \n`base-path` repos and to disallow it for the users.\n\n--*-path and --*-path-fmt are obviously mutually exclusive.\n\nWhat do you think ?\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"25824","messageId":"115637597423-git-send-email-madcoder@debian.org","threadId":"5367","inReplyTo":"7vmz9vgqlm.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-23T23:32:54Z","receivedAt":"2006-08-23T23:32:54Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Allow a form of virtualhosting, when %h format is used.\n\nSigned-off-by: Pierre Habouzit <madcoder@debian.org>\n---\n\n    This is intended to be a more flexible solution, that also gives virtual\n    hosting as a bonus. I still see no way to deal with older clients when\n    virtual hosting is used by the admin though, having a \"default\" hostname\n    won't solve anything at all anyway.\n\n Documentation/git-daemon.txt |   42 ++++++++++\n daemon.c                     |  170 +++++++++++++++++++++++++++++++++++++++---\n 2 files changed, 201 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt\nindex 0f7d274..0aa34d4 100644\n--- a/Documentation/git-daemon.txt\n+++ b/Documentation/git-daemon.txt\n@@ -11,6 +11,7 @@ SYNOPSIS\n 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n              [--timeout=n] [--init-timeout=n] [--strict-paths]\n              [--base-path=path] [--user-path | --user-path=path]\n+             [--base-path-fmt=pathfmt] [--user-path-fmt=pathfmt]\n \t     [--reuseaddr] [--detach] [--pid-file=file] [directory...]\n \n DESCRIPTION\n@@ -45,6 +46,10 @@ OPTIONS\n \t'git://example.com/hello.git', `git-daemon` will interpret the path\n \tas '/srv/git/hello.git'.\n \n+--base-path-fmt=pathfmt::\n+\tWorks like --base-path, but uses a format instead. See Path\n+\tFormats section below.\n+\n --export-all::\n \tAllow pulling from all directories that look like GIT repositories\n \t(have the 'objects' and 'refs' subdirectories), even if they\n@@ -79,6 +84,11 @@ OPTIONS\n \ttaken as a request to access `path/foo` repository in\n \tthe home directory of user `alice`.\n \n+--user-path-fmt=pathfmt::\n+\tAllow ~user notation to be used in requests. The path used to look\n+\tfor the git repository is a format string, see Path Formats\n+\tsection below.\n+\n --verbose::\n \tLog details about the incoming connections and requested files.\n \n@@ -98,6 +108,38 @@ OPTIONS\n \t--strict-paths is specified this will also include subdirectories\n \tof each named directory.\n \n+Path Formats\n+------------\n+\n+%h::\n+\trequested hostname (won't work with clients older than 1.4.0).\n+\n+%p::\n+\trequested path.\n+\n+%P::\n+\trequested path without the first slash when exists (`/`).\n+\n+%u::\n+\trequested username (only allowed for --user-path-fmt).\n+\n+%%::\n+\tplain '%'\n+\n+Examples\n+~~~~~~~~\n+\n+* `--base-path-fmt=/srv/git/%P` emulates `--base-path=/srv/git`\n+\n+* `--base-path-fmt=/srv/git/%h%p` will look into\n+  `/srv/git/git.example.com/foo` if the request goes to\n+  git://git.example.com/foo\n+\n+* `--user-path-fmt=/home/%u/public_git/%P` works mostly like\n+  `--user-path=public_git` (the difference is that it assumes that home\n+  directories live in `/home/username` instead of using the real `username`\n+  home).\n+\n Author\n ------\n Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki\ndiff --git a/daemon.c b/daemon.c\nindex 012936f..1ffebd6 100644\n--- a/daemon.c\n+++ b/daemon.c\n@@ -19,6 +19,7 @@ static const char daemon_usage[] =\n \"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\\n\"\n \"           [--timeout=n] [--init-timeout=n] [--strict-paths]\\n\"\n \"           [--base-path=path] [--user-path | --user-path=path]\\n\"\n+\"           [--base-path-fmt=pathfmt] [--user-path-fmt=pathfmt]\\n\"\n \"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]\";\n \n /* List of acceptable pathname prefixes */\n@@ -29,12 +30,14 @@ static int strict_paths;\n static int export_all_trees;\n \n /* Take all paths relative to this one if non-NULL */\n+int is_base_path_fmt;\n static char *base_path;\n \n /* If defined, ~user notation is allowed and the string is inserted\n  * after ~user/.  E.g. a request to git://host/~alice/frotz would\n  * go to /home/alice/pub_git/frotz with --user-path=pub_git.\n  */\n+int is_user_path_fmt;\n static const char *user_path;\n \n /* Timeout, and initial timeout */\n@@ -148,7 +151,100 @@ static int avoid_alias(char *p)\n \t}\n }\n \n-static char *path_ok(char *dir)\n+static void check_path_fmt(const char *check, int allow_user)\n+{\n+\tconst char *p = check;\n+\n+\twhile (*p) {\n+\t\tif (*p++ != '%')\n+\t\t\tcontinue;\n+\n+\t\tswitch (*p) {\n+\t\t  case '\\0':\n+\t\t    die(\"invalid format: <%s> ends with an unescaped %%\", check);\n+\n+\t\t  case '%':\n+\t\t  case 'p': case 'P':\n+\t\t  case 'h':\n+\t\t    break;\n+\n+\t\t  case 'u':\n+\t\t    if (allow_user)\n+\t\t\t    break;\n+\t\t    /* fallthrough */\n+\n+\t\t  default:\n+\t\t    die(\"invalid format: <%s> uses unknown specifier %%%c\",\n+\t\t\tcheck, *p);\n+\t\t}\n+\n+\t\tp++;\n+\t}\n+}\n+\n+static char *\n+git_path_fmt(char rpath[PATH_MAX], const char *fmt,\n+             const char *vhost, const char *path,\n+             const char *username, int namlen)\n+{\n+\tconst char *p = fmt;\n+\tint pos = 0;\n+\n+\twhile (*p) {\n+\t\tif (*p != '%') {\n+\t\t\trpath[pos++] = *p++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tswitch (*++p) {\n+\t\t  case '%':\n+\t\t    rpath[pos++] = *p;\n+\t\t    break;\n+\n+\t\t  case 'h':\n+                    if (!vhost) {\n+                        logerror(\"missing host=, client is too old\");\n+                        return NULL;\n+                    }\n+\t\t    pos += strlcpy(rpath + pos, vhost, PATH_MAX - pos);\n+\t\t    break;\n+\n+\t\t  case 'P':\n+\t\t    pos += strlcpy(rpath + pos, path + (*path == '/'),\n+\t\t\t\t   PATH_MAX - pos);\n+\t\t    break;\n+\n+\t\t  case 'p':\n+\t\t    pos += strlcpy(rpath + pos, path, PATH_MAX - pos);\n+\t\t    break;\n+\n+\t\t  case 'u':\n+\t\t    pos += snprintf(rpath + pos, PATH_MAX - pos, \"%.*s\",\n+\t\t\t\t    namlen, username);\n+\t\t    break;\n+\t\t}\n+\n+\t\tp++;\n+\n+\t\tif (pos >= PATH_MAX) {\n+                        logerror(\"generated path is too long\");\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\n+\trpath[pos] = '\\0';\n+\n+        return rpath;\n+}\n+\n+static inline char *\n+git_base_path_fmt(char rpath[PATH_MAX], const char *fmt,\n+                  const char *vhost, const char *path)\n+{\n+    return git_path_fmt(rpath, fmt, vhost, path, NULL, 0);\n+}\n+\n+static char *path_ok(char *dir, char *vhost)\n {\n \tstatic char rpath[PATH_MAX];\n \tchar *path;\n@@ -174,10 +270,25 @@ static char *path_ok(char *dir)\n \t\t\t\tslash = dir + restlen;\n \t\t\tnamlen = slash - dir;\n \t\t\trestlen -= namlen;\n-\t\t\tloginfo(\"userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>\", user_path, dir, namlen, restlen, slash);\n-\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s%.*s\",\n-\t\t\t\t namlen, dir, user_path, restlen, slash);\n-\t\t\tdir = rpath;\n+\n+\t\t\tif (is_user_path_fmt) {\n+\t\t\t\tloginfo(\"host <%s>, \"\n+\t\t\t\t\t\"userpathfmt <%s>, request <%s>, \"\n+\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n+\t\t\t\t\tvhost,\n+\t\t\t\t\tuser_path, dir,\n+\t\t\t\t\tnamlen, restlen, slash);\n+                                dir = git_path_fmt(rpath, user_path, vhost,\n+                                                   slash, dir + 1, namlen - 1);\n+\t\t\t} else {\n+\t\t\t\tloginfo(\"userpath <%s>, request <%s>, \"\n+\t\t\t\t\t\"namlen %d, restlen %d, slash <%s>\",\n+\t\t\t\t\tuser_path, dir,\n+\t\t\t\t\tnamlen, restlen, slash);\n+\t\t\t\tsnprintf(rpath, PATH_MAX, \"%.*s/%s%.*s\",\n+\t\t\t\t\t namlen, dir, user_path, restlen, slash);\n+\t\t\t\tdir = rpath;\n+\t\t\t}\n \t\t}\n \t}\n \telse if (base_path) {\n@@ -186,12 +297,18 @@ static char *path_ok(char *dir)\n \t\t\tlogerror(\"'%s': Non-absolute path denied (base-path active)\", dir);\n \t\t\treturn NULL;\n \t\t}\n-\t\telse {\n+\n+\t\tif (is_base_path_fmt) {\n+                        dir = git_base_path_fmt(rpath, base_path, vhost, dir);\n+\t\t} else {\n \t\t\tsnprintf(rpath, PATH_MAX, \"%s%s\", base_path, dir);\n \t\t\tdir = rpath;\n \t\t}\n \t}\n \n+\tif (!dir)\n+\t\treturn NULL;\n+\n \tpath = enter_repo(dir, strict_paths);\n \n \tif (!path) {\n@@ -229,7 +346,7 @@ static char *path_ok(char *dir)\n \treturn NULL;\t\t/* Fallthrough. Deny by default */\n }\n \n-static int upload(char *dir)\n+static int upload(char *dir, char *vhost)\n {\n \t/* Timeout as string */\n \tchar timeout_buf[64];\n@@ -237,7 +354,7 @@ static int upload(char *dir)\n \n \tloginfo(\"Request for '%s'\", dir);\n \n-\tif (!(path = path_ok(dir)))\n+\tif (!(path = path_ok(dir, vhost)))\n \t\treturn -1;\n \n \t/*\n@@ -274,6 +391,7 @@ static int execute(struct sockaddr *addr\n {\n \tstatic char line[1000];\n \tint pktlen, len;\n+\tchar *vhost = NULL;\n \n \tif (addr) {\n \t\tchar addrbuf[256] = \"\";\n@@ -303,15 +421,30 @@ #endif\n \talarm(0);\n \n \tlen = strlen(line);\n-\tif (pktlen != len)\n+\n+\tif (pktlen != len) {\n+\t\tint arg_pos = len + 1;\n+\n \t\tloginfo(\"Extended attributes (%d bytes) exist <%.*s>\",\n \t\t\t(int) pktlen - len,\n-\t\t\t(int) pktlen - len, line + len + 1);\n+\t\t\t(int) pktlen - len, line + arg_pos);\n+\n+\t\twhile (arg_pos < pktlen) {\n+\t\t\tint arg_len = strlen(line + arg_pos);\n+\n+\t\t\tif (!strncmp(\"host=\", line + arg_pos, 5)) {\n+\t\t\t\tvhost = line + arg_pos + 5;\n+\t\t\t}\n+\n+\t\t\targ_pos += arg_len + 1;\n+\t\t}\n+\t}\n+\n \tif (len && line[len-1] == '\\n')\n \t\tline[--len] = 0;\n \n \tif (!strncmp(\"git-upload-pack \", line, 16))\n-\t\treturn upload(line+16);\n+\t\treturn upload(line+16, vhost);\n \n \tlogerror(\"Protocol error: '%s'\", line);\n \treturn -1;\n@@ -768,6 +901,13 @@ int main(int argc, char **argv)\n \t\t}\n \t\tif (!strncmp(arg, \"--base-path=\", 12)) {\n \t\t\tbase_path = arg+12;\n+\t\t\tis_base_path_fmt = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--base-path-fmt=\", 16)) {\n+\t\t\tbase_path = arg+16;\n+\t\t\tis_base_path_fmt = 1;\n+\t\t\tcheck_path_fmt(base_path, 0);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strcmp(arg, \"--reuseaddr\")) {\n@@ -776,10 +916,18 @@ int main(int argc, char **argv)\n \t\t}\n \t\tif (!strcmp(arg, \"--user-path\")) {\n \t\t\tuser_path = \"\";\n+\t\t\tis_user_path_fmt = 0;\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"--user-path=\", 12)) {\n \t\t\tuser_path = arg + 12;\n+\t\t\tis_user_path_fmt = 0;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--user-path-fmt=\", 16)) {\n+\t\t\tuser_path = arg + 16;\n+\t\t\tis_user_path_fmt = 1;\n+\t\t\tcheck_path_fmt(user_path, 1);\n \t\t\tcontinue;\n \t\t}\n \t\tif (!strncmp(arg, \"--pid-file=\", 11)) {\n-- \n1.4.2.g44c10-dirty\n"},{"id":"25827","messageId":"7vbqqbgf79.fsf@assigned-by-dhcp.cox.net","threadId":"5367","inReplyTo":"115637597423-git-send-email-madcoder@debian.org","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-24T00:17:14Z","receivedAt":"2006-08-24T00:17:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pierre Habouzit <madcoder@debian.org> writes:\n\n> Allow a form of virtualhosting, when %h format is used.\n>\n> Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> ---\n>\n>     This is intended to be a more flexible solution, that also gives virtual\n>     hosting as a bonus. I still see no way to deal with older clients when\n>     virtual hosting is used by the admin though, having a \"default\" hostname\n>     won't solve anything at all anyway.\n\nI mildly disagree about the last sentence.  Enabling virtual\nhosting does not have to mean all virtual hosts are treated\nequal.  It is conceivable that a site hosts the primary,\n\"collection of public repositories everybody would want to go\nto\" set, with supplemental ones for specific audiences that are\ndone via virtual hosting.  General public who would want to\naccess the primary one can come with older clients that way, and\nonly the narrower audiences have to be told to upgrade.\n\nThe client-side host= support was done post 1.4.0-rc1 timeframe\nso we have to be nicer to 1.3 based people, at least give them a\nway to slurp newer version with their client ;-).\n\nHaven't looked at the rest of the patch yet.  Will comment\nlater.\n"},{"id":"25835","messageId":"200608240950.08187.madcoder@debian.org","threadId":"5367","inReplyTo":"7vbqqbgf79.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-24T07:50:05Z","receivedAt":"2006-08-24T07:50:05Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Le jeu 24 août 2006 02:17, Junio C Hamano a écrit :\n> Pierre Habouzit <madcoder@debian.org> writes:\n> > Allow a form of virtualhosting, when %h format is used.\n> >\n> > Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> > ---\n> >\n> >     This is intended to be a more flexible solution, that also\n> > gives virtual hosting as a bonus. I still see no way to deal with\n> > older clients when virtual hosting is used by the admin though,\n> > having a \"default\" hostname won't solve anything at all anyway.\n>\n> I mildly disagree about the last sentence.  Enabling virtual\n> hosting does not have to mean all virtual hosts are treated\n> equal.  It is conceivable that a site hosts the primary,\n> \"collection of public repositories everybody would want to go\n> to\" set, with supplemental ones for specific audiences that are\n> done via virtual hosting.  General public who would want to\n> access the primary one can come with older clients that way, and\n> only the narrower audiences have to be told to upgrade.\n\nhmm, yes, that's indeed fair. Well, adding a --default-hostname that is \nused if no host= is passed is completely obvious and straightforward \nand solves that issue.\n\nI also spotted a bug in the git_path_fmt function, I test 'pos' at the \nend of the loop, but the overflow test is not done when there is no \nformat involved, wich could /theorically/ lead to buffer overflow. So \nplease disregard that issue, I will at least provide a patch that fixes \nthat.\n\n> Haven't looked at the rest of the patch yet.  Will comment\n> later.\nnp, TIA\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"25865","messageId":"1156450516.10054.276.camel@cashmere.sps.mot.com","threadId":"5367","inReplyTo":"200608232256.10108.madcoder@debian.org","subject":"Re: [PATCH] git-daemon virtual hosting implementation.","fromName":"Jon Loeliger","fromEmail":"jdl@freescale.com","sentAt":"2006-08-24T20:15:16Z","receivedAt":"2006-08-24T20:15:16Z","isPatch":true,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"On Wed, 2006-08-23 at 15:56, Pierre Habouzit wrote:\n\n> Another option would be not to support virtual hosts, but instead \n> superseed the --base-path and --user-path with some --base-path-fmt \n> and --user-path-fmt where the user can specify how to build the path \n> with simple sprintf-like formats. One could e.g. support:\n>  * %% obviously ;\n>  * %h that will be replaced with the hostname\n>  * %u (only for --user-path-fmt)\n>  * %p (asked path)\n>  * ...\n\nAnd this is exactly what I have implemented and\nrunning on my system today!\n\n> I think that's more clever, and allow more flexible use of the virtual \n> hosting code. It e.g. allow to use the virtual host scheme for the \n> `base-path` repos and to disallow it for the users.\n> \n> --*-path and --*-path-fmt are obviously mutually exclusive.\n> \n> What do you think ?\n\nI kinda like it... :-)\n\njdl\n"},{"id":"25867","messageId":"1156451675.10054.285.camel@cashmere.sps.mot.com","threadId":"5367","inReplyTo":"7vejv598iq.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon virtual hosting implementation.","fromName":"Jon Loeliger","fromEmail":"jdl@freescale.com","sentAt":"2006-08-24T20:34:36Z","receivedAt":"2006-08-24T20:34:36Z","isPatch":true,"sender":{"key":"jdl@jdl.com","avatar":"https://gravatar.com/avatar/75ce9a10b151acd2c28ec4ab2136dba7b2ff1634530bd04b155981a749d08a64?d=mp&s=160"},"body":"On Thu, 2006-08-24 at 15:35, Junio C Hamano wrote:\n> Where is the patch ;-)?\n\nHeh.\n\nWell, I rewrote the interpolation code.  As you pointed\nout earlier, it was crap.  I can send that in; no problem.\n\nThere was some outstanding debate if this was actually\nthe right way to go about things.  Specifically, the\nproblem of canonical hostname.  A proposal was to use\npeer IP addresses instead, and I just haven't gotten\naround to messing with that yet.  So that is why I\nhadn't sent it in yet.  Sorry.\n\nBut, I'll rebase it and send it in this evening.\n\njdl\n"},{"id":"25866","messageId":"7vejv598iq.fsf@assigned-by-dhcp.cox.net","threadId":"5367","inReplyTo":"1156450516.10054.276.camel@cashmere.sps.mot.com","subject":"Re: [PATCH] git-daemon virtual hosting implementation.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-24T20:35:41Z","receivedAt":"2006-08-24T20:35:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jon Loeliger <jdl@freescale.com> writes:\n\n> On Wed, 2006-08-23 at 15:56, Pierre Habouzit wrote:\n>\n>> Another option would be not to support virtual hosts, but instead \n>> superseed the --base-path and --user-path with some --base-path-fmt \n>> and --user-path-fmt where the user can specify how to build the path \n>> with simple sprintf-like formats. One could e.g. support:\n>>  * %% obviously ;\n>>  * %h that will be replaced with the hostname\n>>  * %u (only for --user-path-fmt)\n>>  * %p (asked path)\n>>  * ...\n>\n> And this is exactly what I have implemented and\n> running on my system today!\n\nWhere is the patch ;-)?\n"},{"id":"25976","messageId":"7vfyfiyaex.fsf@assigned-by-dhcp.cox.net","threadId":"5367","inReplyTo":"115637597423-git-send-email-madcoder@debian.org","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-27T06:12:22Z","receivedAt":"2006-08-27T06:12:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pierre Habouzit <madcoder@debian.org> writes:\n\n> Allow a form of virtualhosting, when %h format is used.\n>\n> Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> ---\n>\n>     This is intended to be a more flexible solution, that also gives virtual\n>     hosting as a bonus.\n\nNicely done, almost.\n\nHaving to have the distinction between %p and %P formats feels\nsomewhat unwieldy, though.  Not that I have a better suggestion.\n\n> +int is_base_path_fmt;\n> +int is_user_path_fmt;\n\nI prefer these to be of type \"static int\".\n\nAlthough I am not an authority of variable naming, these sound\nfunny to me.  \"is_XXX()\" as a function name feels natural,\n\"is_XXX\" as a variable name does not --- it is not clear what\nthe predicate is talking about.\n\nMaybe \"use_fmt_for_base_path\" is easier to understand?  I dunno.\nOr \"user_path_is_fmt\"?  That's more logical but still somewhat\nfeels funny.\n"},{"id":"25977","messageId":"200608271228.09718.madcoder@debian.org","threadId":"5367","inReplyTo":"7vfyfiyaex.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-27T10:28:09Z","receivedAt":"2006-08-27T10:28:09Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Le dim 27 août 2006 08:12, Junio C Hamano a écrit :\n> Pierre Habouzit <madcoder@debian.org> writes:\n> > Allow a form of virtualhosting, when %h format is used.\n> >\n> > Signed-off-by: Pierre Habouzit <madcoder@debian.org>\n> > ---\n> >\n> >     This is intended to be a more flexible solution, that also\n> > gives virtual hosting as a bonus.\n>\n> Nicely done, almost.\n>\n> Having to have the distinction between %p and %P formats feels\n> somewhat unwieldy, though.  Not that I have a better suggestion.\n>\n> > +int is_base_path_fmt;\n> > +int is_user_path_fmt;\n>\n> I prefer these to be of type \"static int\".\n\nomg, how did I missed that.\n\n> Although I am not an authority of variable naming, these sound\n> funny to me.  \"is_XXX()\" as a function name feels natural,\n> \"is_XXX\" as a variable name does not --- it is not clear what\n> the predicate is talking about.\n>\n> Maybe \"use_fmt_for_base_path\" is easier to understand?  I dunno.\n> Or \"user_path_is_fmt\"?  That's more logical but still somewhat\n> feels funny.\n\nagreed.\n\nThere is also a second patch that never made it to the list that fixes:\n * some indentation problems due to a bad vimrc\n * --default-hostname switch (to handle virtual hosts even with older\n   clients)\n * possible overflow in the formatting method.\n\nI'll recompute a new patch that superseeds that one, and merge your \ncomments and my never sent patch too.\n\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"25978","messageId":"7v64gexxgl.fsf@assigned-by-dhcp.cox.net","threadId":"5367","inReplyTo":"200608271228.09718.madcoder@debian.org","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-08-27T10:52:10Z","receivedAt":"2006-08-27T10:52:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Pierre Habouzit <madcoder@debian.org> writes:\n\n> There is also a second patch that never made it to the list that fixes:\n>  * some indentation problems due to a bad vimrc\n>  * --default-hostname switch (to handle virtual hosts even with older\n>    clients)\n>  * possible overflow in the formatting method.\n>\n> I'll recompute a new patch that superseeds that one, and merge your \n> comments and my never sent patch too.\n\nI have to admit that I kinda liked JDL's simpler one first (and\nit has been in production use for some time).  We'll see.\n\nAbout vger potentially throwing things away, I use this script\n(called \"taboo.perl\") to check my messages before sending them\nout.\n\nObviously the taboo-word list itself is not attached here, but\nthe actual script should have a copy of it after the __DATA__\nmarker.\n\n-- >8 --\n\n#!/usr/bin/perl -w\n\nmy $tmpl = '\tif (%%PATTERN%%) {\n\t\tprint \"$lineno ${_}matches %%QPATTERN%%\\n\";\n\t\treturn;\n\t}\n';\nmy $stmt = \"\";\nmy $in_header = 1;\n\nwhile (<DATA>) {\n\tif (/^\\$global_taboo_body =/) {\n\t\t$in_header = 0;\n\t}\n\tnext if (/^\\043/ || /^\\$/ || /^END$/ || /^\\s*$/);\n\tchomp;\n\tmy $p = $_;\n\tif ($in_header) {\n\t\t$p = '/^[-\\w_]*:/ && ' . $p;\n\t}\n\tmy $q = quotemeta($p);\n\tmy $stmt1 = $tmpl;\n\t$stmt1 =~ s|%%PATTERN%%|$p|g;\n\t$stmt1 =~ s|%%QPATTERN%%|$q|g;\n\t$stmt .= $stmt1;\n}\nclose DATA;\n\n$stmt = 'sub check {\n\tmy ($line, $lineno) = @_;\n' . $stmt . '\n}\n';\neval $stmt;\nwhile (<>) {\n\tcheck($_, $.);\n}\n\nmy $how_to_update_this_script = <<'EOF' ;\n\t( sed -e '/^__DATA__$$/q' taboo.perl && \\\n\t  wget -q -O - http://vger.kernel.org/majordomo-taboos.txt ) \\\n\t\t>taboo.perl+\n\tif diff -u taboo.perl taboo.perl+; \\\n\tthen \\\n\t\trm -f taboo.perl+; \\\n\t\techo >&2 No changes.; \\\n\telse \\\n\t\tmv taboo.perl+ taboo.perl; \\\n\t\tchmod +x taboo.perl; \\\n\tfi\nEOF\n\n__DATA__\n"},{"id":"25983","messageId":"200608271340.32792.madcoder@debian.org","threadId":"5367","inReplyTo":"7v64gexxgl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Pierre Habouzit","fromEmail":"madcoder@debian.org","sentAt":"2006-08-27T11:40:32Z","receivedAt":"2006-08-27T11:40:32Z","isPatch":true,"sender":{"key":"madcoder@debian.org","avatar":"https://avatars.githubusercontent.com/u/44708?v=4"},"body":"Le dim 27 août 2006 12:52, Junio C Hamano a écrit :\n> About vger potentially throwing things away, I use this script\n> (called \"taboo.perl\") to check my messages before sending them\n> out.\n\nthat was not it, I was biten (again) by git-send-mail that uses strftime \n(localized) to generate rfc822 dates, making them unparseable :|\n\nI've resent the aggregated patch, that should work right now.\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"},{"id":"25988","messageId":"ecsdqa$1pi$1@sea.gmane.org","threadId":"5367","inReplyTo":"200608271340.32792.madcoder@debian.org","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-08-27T15:30:24Z","receivedAt":"2006-08-27T15:30:24Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Pierre Habouzit wrote:\n\n> Le dim 27 ao?t 2006 12:52, Junio C Hamano a écrit :\n>> About vger potentially throwing things away, I use this script\n>> (called \"taboo.perl\") to check my messages before sending them\n>> out.\n> \n> that was not it, I was biten (again) by git-send-mail that uses strftime \n> (localized) to generate rfc822 dates, making them unparseable :|\n\nUpdate your git, or use ./git-send-email.perl directly from git repository\n(from 'master' branch, as 'next' branch version uses Git.pm). Strftime was\nreplaced by pure Perl to generate rfc2822 date some time ago...\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"25991","messageId":"864pvyi2nr.fsf@blue.stonehenge.com","threadId":"5367","inReplyTo":"7v64gexxgl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Randal L. Schwartz","fromEmail":"merlyn@stonehenge.com","sentAt":"2006-08-27T16:06:32Z","receivedAt":"2006-08-27T16:06:32Z","isPatch":true,"sender":{"key":"merlyn@stonehenge.com","avatar":"https://gravatar.com/avatar/dc528d210743ff0333e6213f9ee7b33b23f1b7bc1f3c5a8c2d819074ecd7ab19?d=mp&s=160"},"body":">>>>> \"Junio\" == Junio C Hamano <junkio@cox.net> writes:\n\nJunio> About vger potentially throwing things away, I use this script\nJunio> (called \"taboo.perl\") to check my messages before sending them\nJunio> out.\n\nJunio> Obviously the taboo-word list itself is not attached here, but\nJunio> the actual script should have a copy of it after the __DATA__\nJunio> marker.\n\nWith \"Inline::Files\" from the CPAN, you could have a switch where the\nscript updates itself with the new list:\n\n    use Inline::Files;\n    if (@ARGV == 1 and $ARGV[0] eq \"-update\") {\n      use LWP::Simple;\n      my $list = get \"http://example.com/foo/bar.txt\";\n      open DATA, \">$DATA\" or die \"cannot write myself: $!\";\n      print DATA $list;\n      close DATA;\n      exit 0;\n    }\n\n    ... rest of your program here ...\n    ... read using <DATA> as before ...\n\n    __DATA__\n    the list will magically go here.\n\n-- \nRandal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095\n<merlyn@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>\nPerl/Unix/security consulting, Technical writing, Comedy, etc. etc.\nSee PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!\n"},{"id":"25992","messageId":"200608271826.48155.pierre.habouzit@m4x.org","threadId":"5367","inReplyTo":"ecsdqa$1pi$1@sea.gmane.org","subject":"Re: [PATCH] git-daemon: more powerful base-path/user-path settings, using formats.","fromName":"Pierre Habouzit","fromEmail":"pierre.habouzit@m4x.org","sentAt":"2006-08-27T16:26:45Z","receivedAt":"2006-08-27T16:26:45Z","isPatch":true,"sender":{"key":"pierre.habouzit@m4x.org","avatar":null},"body":"Le dim 27 août 2006 17:30, Jakub Narebski a écrit :\n> Pierre Habouzit wrote:\n> > Le dim 27 ao?t 2006 12:52, Junio C Hamano a écrit :\n> >> About vger potentially throwing things away, I use this script\n> >> (called \"taboo.perl\") to check my messages before sending them\n> >> out.\n> >\n> > that was not it, I was biten (again) by git-send-mail that uses\n> > strftime (localized) to generate rfc822 dates, making them\n> > unparseable :|\n>\n> Update your git, or use ./git-send-email.perl directly from git\n> repository (from 'master' branch, as 'next' branch version uses\n> Git.pm). Strftime was replaced by pure Perl to generate rfc2822 date\n> some time ago...\n\nI know that, I've done that on many of my system, but not the one where \nI hack git ... how lame isn't it ? :)\n-- \n·O·  Pierre Habouzit\n··O                                                madcoder@debian.org\nOOO                                                http://www.madism.org\n"}]}