~ubuntu-branches/ubuntu/oneiric/viewvc/oneiric-security

« back to all changes in this revision

Viewing changes to lib/vclib/ccvs/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): David Martínez Moreno, John Zaitseff, David Martínez Moreno
  • Date: 2010-07-02 02:24:34 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20100702022434-9a5shavr4tj4cfqy
Tags: 1.1.5-1
[ John Zaitseff ]
* New upstream release (closes: #532611, #575777, #575787, #576307).  This
  solves CVE-2010-0004, CVE-2010-0005, CVE-2010-0736 and CVE-2010-0132.
* Extensive rewrite of files in the debian directory.  Updated to Debian
  policy 3.8.4, updated all control files to Debhelper 7, rewrote
  debian/rules for clarity (and to use Debhelper 7).
* Removed all references to Debconf, as previous versions of this
  package violated Debian policy (section 10.7.3): /etc/viewvc/viewvc.conf
  is a conffile, and maintainer scripts must NOT modify it at any time.
* Reorganised the installation files in /usr/lib/viewvc.  The CGI
  programs are now links to files in /usr/lib/viewvc/cgi-bin.
* Packaged the Apache mod-python modules for optional use (in
  /usr/lib/viewvc/mod-python).  See README.Debian for more information.
* Moved the static help documentation ("docroot") from /usr/share/viewvc
  to /usr/share/viewvc/docroot, as per Webapps Policy, section 3.1.
* Updated the debian/patches subdirectory to remove patches no longer
  relevant to ViewVC 1.1.x and to update those that still apply.
* debian/control:
  - Removed the dependency on gawk, as that was only required for Debconf
    configuration.
  - Demoted the dependency on mime-support to "Suggests": ViewVC can use
    it, if appropriately configured, but does not require it.
  - Added a suggestion for the python-tk package: viewvc-standalone(1)
    uses this when passed the "--gui" flag.
  - Modified all dependencies as appropriate.  Depend on httpd-cgi, not
    httpd, since the viewvc package needs a CGI server.  In addition,
    python-egenix-mxdatetime is no longer needed (since ViewVC 1.0.x).
  - Updated the XS-Python-Version field to "all" (Closes: #570573).
  - ViewVC 1.1.x supports only python-pygments as a syntax highlighter,
    not enscript.  Adjusted dependencies as appropriate.

[ David Martínez Moreno ]
* Changed history and added the CVE entry to the changelog for 1.0.9-1.
* debian/control:
  - Moved Section to vcs in order to match the overrides.
  - Make python-dev dependency just python.
  - Removed dummy package viewcvs, it was already dummy in lenny.
* debian/viewcvs.*: Removed.
* debian/NEWS: Fixed version in John's entry and removed old news from 0.9.4.
* debian/README.source: Added.
* The new release also addresses in a different way how to show long
  annotation messages (closes: #434301).
* Added debian/patches/92-no_strings_in_raise for fixing a couple of
  occurrences of string exceptions in the code, no longer valid in Python
  2.6, the default now (closes: #585366).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# -*-python-*-
2
2
#
3
 
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
 
3
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
4
4
#
5
5
# By using this file, you agree to the terms and conditions set forth in
6
6
# the LICENSE.html file which can be found at the top level of the ViewVC
9
9
# For more information, visit http://viewvc.org/
10
10
#
11
11
# -----------------------------------------------------------------------
12
 
 
13
 
"""
14
 
This is a Version Control library driver for locally accessible cvs-repositories.
15
 
"""
16
 
 
17
12
import os
18
 
import string
19
 
import re
20
 
import cStringIO
21
 
import tempfile
22
 
 
23
 
import vclib
24
 
import rcsparse
25
 
import blame
26
 
 
27
 
### The functionality shared with bincvs should probably be moved to a
28
 
### separate module
29
 
from vclib.bincvs import CVSRepository, Revision, Tag, \
30
 
                         _file_log, _log_path
31
 
 
32
 
class CCVSRepository(CVSRepository):
33
 
  def dirlogs(self, path_parts, rev, entries, options):
34
 
    """see vclib.Repository.dirlogs docstring
35
 
 
36
 
    rev can be a tag name or None. if set only information from revisions
37
 
    matching the tag will be retrieved
38
 
 
39
 
    Option values recognized by this implementation:
40
 
 
41
 
      cvs_subdirs
42
 
        boolean. true to fetch logs of the most recently modified file in each
43
 
        subdirectory
44
 
 
45
 
    Option values returned by this implementation:
46
 
 
47
 
      cvs_tags, cvs_branches
48
 
        lists of tag and branch names encountered in the directory
49
 
    """
50
 
    subdirs = options.get('cvs_subdirs', 0)
51
 
 
52
 
    dirpath = self._getpath(path_parts)
53
 
    alltags = {           # all the tags seen in the files of this dir
54
 
      'MAIN' : '',
55
 
      'HEAD' : '1.1'
56
 
    }
57
 
 
58
 
    for entry in entries:
59
 
      entry.rev = entry.date = entry.author = entry.dead = entry.log = None
60
 
      path = _log_path(entry, dirpath, subdirs)
61
 
      if path:
62
 
        entry.path = path
63
 
        try:
64
 
          rcsparse.Parser().parse(open(path, 'rb'), InfoSink(entry, rev, alltags))
65
 
        except IOError, e:
66
 
          entry.errors.append("rcsparse error: %s" % e)
67
 
        except RuntimeError, e:
68
 
          entry.errors.append("rcsparse error: %s" % e)
69
 
        except rcsparse.RCSStopParser:
70
 
          pass
71
 
 
72
 
    branches = options['cvs_branches'] = []
73
 
    tags = options['cvs_tags'] = []
74
 
    for name, rev in alltags.items():
75
 
      if Tag(None, rev).is_branch:
76
 
        branches.append(name)
77
 
      else:
78
 
        tags.append(name)
79
 
 
80
 
  def itemlog(self, path_parts, rev, options):
81
 
    """see vclib.Repository.itemlog docstring
82
 
 
83
 
    rev parameter can be a revision number, a branch number, a tag name,
84
 
    or None. If None, will return information about all revisions, otherwise,
85
 
    will only return information about the specified revision or branch.
86
 
 
87
 
    Option values returned by this implementation:
88
 
 
89
 
      cvs_tags
90
 
        dictionary of Tag objects for all tags encountered
91
 
    """
92
 
    path = self.rcsfile(path_parts, 1)
93
 
    sink = TreeSink()
94
 
    rcsparse.Parser().parse(open(path, 'rb'), sink)
95
 
    filtered_revs = _file_log(sink.revs.values(), sink.tags,
96
 
                              sink.default_branch, rev)
97
 
    for rev in filtered_revs:
98
 
      if rev.prev and len(rev.number) == 2:
99
 
        rev.changed = rev.prev.next_changed
100
 
    options['cvs_tags'] = sink.tags
101
 
 
102
 
    return filtered_revs
103
 
 
104
 
  def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
105
 
    temp1 = tempfile.mktemp()
106
 
    open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
107
 
    temp2 = tempfile.mktemp()
108
 
    open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
109
 
 
110
 
    r1 = self.itemlog(path_parts1, rev1, {})[-1]
111
 
    r2 = self.itemlog(path_parts2, rev2, {})[-1]
112
 
 
113
 
    info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
114
 
    info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)
115
 
 
116
 
    diff_args = vclib._diff_args(type, options)
117
 
 
118
 
    return vclib._diff_fp(temp1, temp2, info1, info2, diff_args)
119
 
 
120
 
  def annotate(self, path_parts, rev=None):
121
 
    source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
122
 
    return source, source.revision
123
 
 
124
 
  def openfile(self, path_parts, rev=None):
125
 
    path = self.rcsfile(path_parts, 1)
126
 
    sink = COSink(rev)
127
 
    rcsparse.Parser().parse(open(path, 'rb'), sink)
128
 
    revision = sink.last and sink.last.string
129
 
    return cStringIO.StringIO(string.join(sink.sstext.text, "\n")), revision
130
 
 
131
 
class MatchingSink(rcsparse.Sink):
132
 
  """Superclass for sinks that search for revisions based on tag or number"""
133
 
 
134
 
  def __init__(self, find):
135
 
    """Initialize with tag name or revision number string to match against"""
136
 
    if not find or find == 'MAIN' or find == 'HEAD':
137
 
      self.find = None
138
 
    else:
139
 
      self.find = find
140
 
 
141
 
    self.find_tag = None
142
 
 
143
 
  def set_principal_branch(self, branch_number):
144
 
    if self.find is None:
145
 
      self.find_tag = Tag(None, branch_number)
146
 
 
147
 
  def define_tag(self, name, revision):
148
 
    if name == self.find:
149
 
      self.find_tag = Tag(None, revision)
150
 
 
151
 
  def admin_completed(self):
152
 
    if self.find_tag is None:
153
 
      if self.find is None:
154
 
        self.find_tag = Tag(None, '')
155
 
      else:
156
 
        try:
157
 
          self.find_tag = Tag(None, self.find)
158
 
        except ValueError:
159
 
          pass
160
 
 
161
 
class InfoSink(MatchingSink):
162
 
  def __init__(self, entry, tag, alltags):
163
 
    MatchingSink.__init__(self, tag)
164
 
    self.entry = entry
165
 
    self.alltags = alltags
166
 
    self.matching_rev = None
167
 
    self.perfect_match = 0
168
 
 
169
 
  def define_tag(self, name, revision):
170
 
    MatchingSink.define_tag(self, name, revision)
171
 
    self.alltags[name] = revision
172
 
 
173
 
  def admin_completed(self):
174
 
    MatchingSink.admin_completed(self)
175
 
    if self.find_tag is None:
176
 
      # tag we're looking for doesn't exist
177
 
      raise rcsparse.RCSStopParser
178
 
 
179
 
  def define_revision(self, revision, date, author, state, branches, next):
180
 
    if self.perfect_match:
181
 
      return
182
 
 
183
 
    tag = self.find_tag
184
 
    rev = Revision(revision, date, author, state == "dead")
185
 
 
186
 
    # perfect match if revision number matches tag number or if revision is on
187
 
    # trunk and tag points to trunk. imperfect match if tag refers to a branch
188
 
    # and this revision is the highest revision so far found on that branch
189
 
    perfect = ((rev.number == tag.number) or
190
 
               (not tag.number and len(rev.number) == 2))
191
 
    if perfect or (tag.is_branch and tag.number == rev.number[:-1] and
192
 
                   (not self.matching_rev or
193
 
                    rev.number > self.matching_rev.number)):
194
 
      self.matching_rev = rev
195
 
      self.perfect_match = perfect
196
 
 
197
 
  def set_revision_info(self, revision, log, text):
198
 
    if self.matching_rev:
199
 
      if revision == self.matching_rev.string:
200
 
        self.entry.rev = self.matching_rev.string
201
 
        self.entry.date = self.matching_rev.date
202
 
        self.entry.author = self.matching_rev.author
203
 
        self.entry.dead = self.matching_rev.dead
204
 
        self.entry.log = log
205
 
        raise rcsparse.RCSStopParser
206
 
    else:
207
 
      raise rcsparse.RCSStopParser
208
 
 
209
 
class TreeSink(rcsparse.Sink):
210
 
  d_command = re.compile('^d(\d+)\\s(\\d+)')
211
 
  a_command = re.compile('^a(\d+)\\s(\\d+)')
212
 
 
213
 
  def __init__(self):
214
 
    self.revs = { }
215
 
    self.tags = { }
216
 
    self.head = None
217
 
    self.default_branch = None
218
 
 
219
 
  def set_head_revision(self, revision):
220
 
    self.head = revision
221
 
 
222
 
  def set_principal_branch(self, branch_number):
223
 
    self.default_branch = branch_number
224
 
 
225
 
  def define_tag(self, name, revision):
226
 
    # check !tags.has_key(tag_name)
227
 
    self.tags[name] = revision
228
 
 
229
 
  def define_revision(self, revision, date, author, state, branches, next):
230
 
    # check !revs.has_key(revision)
231
 
    self.revs[revision] = Revision(revision, date, author, state == "dead")
232
 
 
233
 
  def set_revision_info(self, revision, log, text):
234
 
    # check revs.has_key(revision)
235
 
    rev = self.revs[revision]
236
 
    rev.log = log
237
 
 
238
 
    changed = None
239
 
    added = 0
240
 
    deled = 0
241
 
    if self.head != revision:
242
 
      changed = 1
243
 
      lines = string.split(text, '\n')
244
 
      idx = 0
245
 
      while idx < len(lines):
246
 
        command = lines[idx]
247
 
        dmatch = self.d_command.match(command)
248
 
        idx = idx + 1
249
 
        if dmatch:
250
 
          deled = deled + string.atoi(dmatch.group(2))
251
 
        else:
252
 
          amatch = self.a_command.match(command)
253
 
          if amatch:
254
 
            count = string.atoi(amatch.group(2))
255
 
            added = added + count
256
 
            idx = idx + count
257
 
          elif command:
258
 
            raise "error while parsing deltatext: %s" % command
259
 
 
260
 
    if len(rev.number) == 2:
261
 
      rev.next_changed = changed and "+%i -%i" % (deled, added)
262
 
    else:
263
 
      rev.changed = changed and "+%i -%i" % (added, deled)
264
 
 
265
 
class StreamText:
266
 
  d_command = re.compile('^d(\d+)\\s(\\d+)')
267
 
  a_command = re.compile('^a(\d+)\\s(\\d+)')
268
 
 
269
 
  def __init__(self, text):
270
 
    self.text = string.split(text, "\n")
271
 
 
272
 
  def command(self, cmd):
273
 
    adjust = 0
274
 
    add_lines_remaining = 0
275
 
    diffs = string.split(cmd, "\n")
276
 
    if diffs[-1] == "":
277
 
      del diffs[-1]
278
 
    if len(diffs) == 0:
279
 
      return
280
 
    if diffs[0] == "":
281
 
      del diffs[0]
282
 
    for command in diffs:
283
 
      if add_lines_remaining > 0:
284
 
        # Insertion lines from a prior "a" command
285
 
        self.text.insert(start_line + adjust, command)
286
 
        add_lines_remaining = add_lines_remaining - 1
287
 
        adjust = adjust + 1
288
 
        continue
289
 
      dmatch = self.d_command.match(command)
290
 
      amatch = self.a_command.match(command)
291
 
      if dmatch:
292
 
        # "d" - Delete command
293
 
        start_line = string.atoi(dmatch.group(1))
294
 
        count      = string.atoi(dmatch.group(2))
295
 
        begin = start_line + adjust - 1
296
 
        del self.text[begin:begin + count]
297
 
        adjust = adjust - count
298
 
      elif amatch:
299
 
        # "a" - Add command
300
 
        start_line = string.atoi(amatch.group(1))
301
 
        count      = string.atoi(amatch.group(2))
302
 
        add_lines_remaining = count
303
 
      else:
304
 
        raise RuntimeError, 'Error parsing diff commands'
305
 
 
306
 
def secondnextdot(s, start):
307
 
  # find the position the second dot after the start index.
308
 
  return string.find(s, '.', string.find(s, '.', start) + 1)
309
 
 
310
 
 
311
 
class COSink(MatchingSink):
312
 
  def __init__(self, rev):
313
 
    MatchingSink.__init__(self, rev)
314
 
 
315
 
  def set_head_revision(self, revision):
316
 
    self.head = Revision(revision)
317
 
    self.last = None
318
 
    self.sstext = None
319
 
 
320
 
  def admin_completed(self):
321
 
    MatchingSink.admin_completed(self)
322
 
    if self.find_tag is None:
323
 
      raise vclib.InvalidRevision(self.find)
324
 
 
325
 
  def set_revision_info(self, revision, log, text):
326
 
    tag = self.find_tag
327
 
    rev = Revision(revision)
328
 
 
329
 
    if rev.number == tag.number:
330
 
      self.log = log
331
 
 
332
 
    depth = len(rev.number)
333
 
 
334
 
    if rev.number == self.head.number:
335
 
      assert self.sstext is None
336
 
      self.sstext = StreamText(text)
337
 
    elif (depth == 2 and tag.number and rev.number >= tag.number[:depth]):
338
 
      assert len(self.last.number) == 2
339
 
      assert rev.number < self.last.number
340
 
      self.sstext.command(text)
341
 
    elif (depth > 2 and rev.number[:depth-1] == tag.number[:depth-1] and
342
 
          (rev.number <= tag.number or len(tag.number) == depth-1)):
343
 
      assert len(rev.number) - len(self.last.number) in (0, 2)
344
 
      assert rev.number > self.last.number
345
 
      self.sstext.command(text)
346
 
    else:
347
 
      rev = None
348
 
 
349
 
    if rev:
350
 
      #print "tag =", tag.number, "rev =", rev.number, "<br>"
351
 
      self.last = rev
 
13
import os.path
 
14
 
 
15
 
 
16
def canonicalize_rootpath(rootpath):
 
17
  return os.path.normpath(rootpath)
 
18
 
 
19
 
 
20
def expand_root_parent(parent_path):
 
21
  # Each subdirectory of PARENT_PATH that contains a child
 
22
  # "CVSROOT/config" is added the set of returned roots.  Or, if the
 
23
  # PARENT_PATH itself contains a child "CVSROOT/config", then all its
 
24
  # subdirectories are returned as roots.
 
25
  roots = {}
 
26
  subpaths = os.listdir(parent_path)
 
27
  cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))
 
28
  for rootname in subpaths:
 
29
    rootpath = os.path.join(parent_path, rootname)
 
30
    if cvsroot \
 
31
       or (os.path.exists(os.path.join(rootpath, "CVSROOT", "config"))):
 
32
      roots[rootname] = canonicalize_rootpath(rootpath)
 
33
  return roots
 
34
 
 
35
 
 
36
def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse):
 
37
  rootpath = canonicalize_rootpath(rootpath)
 
38
  if use_rcsparse:
 
39
    import ccvs
 
40
    return ccvs.CCVSRepository(name, rootpath, authorizer, utilities)
 
41
  else:
 
42
    import bincvs
 
43
    return bincvs.BinCVSRepository(name, rootpath, authorizer, utilities)