14 """ |
14 """ |
15 |
15 |
16 from i18n import _ |
16 from i18n import _ |
17 import error, osutil, encoding |
17 import error, osutil, encoding |
18 import errno, re, shutil, sys, tempfile, traceback |
18 import errno, re, shutil, sys, tempfile, traceback |
19 import os, stat, time, calendar, textwrap, unicodedata, signal |
19 import os, time, calendar, textwrap, unicodedata, signal |
20 import imp, socket |
20 import imp, socket |
21 |
21 |
22 # Python compatibility |
22 # Python compatibility |
23 |
23 |
24 def sha1(s): |
24 def sha1(s): |
490 t = n[-1] |
490 t = n[-1] |
491 if t in '. ': |
491 if t in '. ': |
492 return _("filename ends with '%s', which is not allowed " |
492 return _("filename ends with '%s', which is not allowed " |
493 "on Windows") % t |
493 "on Windows") % t |
494 |
494 |
495 class path_auditor(object): |
|
496 '''ensure that a filesystem path contains no banned components. |
|
497 the following properties of a path are checked: |
|
498 |
|
499 - ends with a directory separator |
|
500 - under top-level .hg |
|
501 - starts at the root of a windows drive |
|
502 - contains ".." |
|
503 - traverses a symlink (e.g. a/symlink_here/b) |
|
504 - inside a nested repository (a callback can be used to approve |
|
505 some nested repositories, e.g., subrepositories) |
|
506 ''' |
|
507 |
|
508 def __init__(self, root, callback=None): |
|
509 self.audited = set() |
|
510 self.auditeddir = set() |
|
511 self.root = root |
|
512 self.callback = callback |
|
513 |
|
514 def __call__(self, path): |
|
515 '''Check the relative path. |
|
516 path may contain a pattern (e.g. foodir/**.txt)''' |
|
517 |
|
518 if path in self.audited: |
|
519 return |
|
520 # AIX ignores "/" at end of path, others raise EISDIR. |
|
521 if endswithsep(path): |
|
522 raise Abort(_("path ends in directory separator: %s") % path) |
|
523 normpath = os.path.normcase(path) |
|
524 parts = splitpath(normpath) |
|
525 if (os.path.splitdrive(path)[0] |
|
526 or parts[0].lower() in ('.hg', '.hg.', '') |
|
527 or os.pardir in parts): |
|
528 raise Abort(_("path contains illegal component: %s") % path) |
|
529 if '.hg' in path.lower(): |
|
530 lparts = [p.lower() for p in parts] |
|
531 for p in '.hg', '.hg.': |
|
532 if p in lparts[1:]: |
|
533 pos = lparts.index(p) |
|
534 base = os.path.join(*parts[:pos]) |
|
535 raise Abort(_('path %r is inside nested repo %r') |
|
536 % (path, base)) |
|
537 |
|
538 parts.pop() |
|
539 prefixes = [] |
|
540 while parts: |
|
541 prefix = os.sep.join(parts) |
|
542 if prefix in self.auditeddir: |
|
543 break |
|
544 curpath = os.path.join(self.root, prefix) |
|
545 try: |
|
546 st = os.lstat(curpath) |
|
547 except OSError, err: |
|
548 # EINVAL can be raised as invalid path syntax under win32. |
|
549 # They must be ignored for patterns can be checked too. |
|
550 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL): |
|
551 raise |
|
552 else: |
|
553 if stat.S_ISLNK(st.st_mode): |
|
554 raise Abort(_('path %r traverses symbolic link %r') % |
|
555 (path, prefix)) |
|
556 elif (stat.S_ISDIR(st.st_mode) and |
|
557 os.path.isdir(os.path.join(curpath, '.hg'))): |
|
558 if not self.callback or not self.callback(curpath): |
|
559 raise Abort(_('path %r is inside nested repo %r') % |
|
560 (path, prefix)) |
|
561 prefixes.append(prefix) |
|
562 parts.pop() |
|
563 |
|
564 self.audited.add(path) |
|
565 # only add prefixes to the cache after checking everything: we don't |
|
566 # want to add "foo/bar/baz" before checking if there's a "foo/.hg" |
|
567 self.auditeddir.update(prefixes) |
|
568 |
|
569 def lookup_reg(key, name=None, scope=None): |
495 def lookup_reg(key, name=None, scope=None): |
570 return None |
496 return None |
571 |
497 |
572 def hidewindow(): |
498 def hidewindow(): |
573 """Hide current shell window. |
499 """Hide current shell window. |