1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

# This file is part of Buildbot.  Buildbot is free software: you can 

# redistribute it and/or modify it under the terms of the GNU General Public 

# License as published by the Free Software Foundation, version 2. 

# 

# This program is distributed in the hope that it will be useful, but WITHOUT 

# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 

# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 

# details. 

# 

# You should have received a copy of the GNU General Public License along with 

# this program; if not, write to the Free Software Foundation, Inc., 51 

# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

# 

# Copyright Buildbot Team Members 

 

 

from twisted.python import log 

from twisted.internet import defer 

 

from buildbot.pbutil import NewCredPerspective 

from buildbot.changes import base 

from buildbot.util import epoch2datetime 

from buildbot import config 

 

class ChangePerspective(NewCredPerspective): 

 

    def __init__(self, master, prefix): 

        self.master = master 

        self.prefix = prefix 

 

    def attached(self, mind): 

        return self 

    def detached(self, mind): 

        pass 

 

    def perspective_addChange(self, changedict): 

        log.msg("perspective_addChange called") 

 

        if 'revlink' in changedict and not changedict['revlink']: 

            changedict['revlink'] = '' 

        if 'repository' in changedict and not changedict['repository']: 

            changedict['repository'] = '' 

        if 'project' in changedict and not changedict['project']: 

            changedict['project'] = '' 

        if 'files' not in changedict or not changedict['files']: 

            changedict['files'] = [] 

 

        # rename arguments to new names.  Note that the client still uses the 

        # "old" names (who, when, and isdir), as they are not deprecated yet, 

        # although the master will accept the new names (author, 

        # when_timestamp, and is_dir).  After a few revisions have passed, we 

        # can switch the client to use the new names. 

        if 'isdir' in changedict: 

            changedict['is_dir'] = changedict['isdir'] 

            del changedict['isdir'] 

        if 'who' in changedict: 

            changedict['author'] = changedict['who'] 

            del changedict['who'] 

        if 'when' in changedict: 

            when = None 

            if changedict['when'] is not None: 

                when = epoch2datetime(changedict['when']) 

            changedict['when_timestamp'] = when 

            del changedict['when'] 

 

        # turn any bytestring keys into unicode, assuming utf8 but just 

        # replacing unknown characters.  Ideally client would send us unicode 

        # in the first place, but older clients do not, so this fallback is 

        # useful. 

        for key in changedict: 

            if type(changedict[key]) == str: 

                changedict[key] = changedict[key].decode('utf8', 'replace') 

        changedict['files'] = list(changedict['files']) 

        for i, file in enumerate(changedict.get('files', [])): 

            if type(file) == str: 

                changedict['files'][i] = file.decode('utf8', 'replace') 

 

        files = [] 

        for path in changedict['files']: 

            if self.prefix: 

                if not path.startswith(self.prefix): 

                    # this file does not start with the prefix, so ignore it 

                    continue 

                path = path[len(self.prefix):] 

            files.append(path) 

        changedict['files'] = files 

 

        if not files: 

            log.msg("No files listed in change... bit strange, but not fatal.") 

        d = self.master.addChange(**changedict) 

        # since this is a remote method, we can't return a Change instance, so 

        # this just sets the return value to None: 

        d.addCallback(lambda _ : None) 

        return d 

 

class PBChangeSource(config.ReconfigurableServiceMixin, base.ChangeSource): 

    compare_attrs = ["user", "passwd", "port", "prefix", "port"] 

 

    def __init__(self, user="change", passwd="changepw", port=None, 

            prefix=None): 

 

        self.user = user 

        self.passwd = passwd 

        self.port = port 

        self.prefix = prefix 

        self.registration = None 

        self.registered_port = None 

 

    def describe(self): 

        portname = self.registered_port 

        d = "PBChangeSource listener on " + str(portname) 

        if self.prefix is not None: 

            d += " (prefix '%s')" % self.prefix 

        return d 

 

    @defer.inlineCallbacks 

    def reconfigService(self, new_config): 

        # calculate the new port 

        port = self.port 

        if port is None: 

            port = new_config.slavePortnum 

 

        # and, if it's changed, re-register 

        if port != self.registered_port: 

            yield self._unregister() 

            self._register(port) 

 

        yield config.ReconfigurableServiceMixin.reconfigService( 

                self, new_config) 

 

    def stopService(self): 

        d = defer.maybeDeferred(base.ChangeSource.stopService, self) 

        d.addCallback(lambda _ : self._unregister()) 

        return d 

 

    def _register(self, port): 

        if not port: 

            log.msg("PBChangeSource has no port to listen on") 

            return 

        self.registered_port = port 

        self.registration = self.master.pbmanager.register( 

                port, self.user, self.passwd, 

                self.getPerspective) 

 

    def _unregister(self): 

        self.registered_port = None 

        if self.registration: 

            return self.registration.unregister() 

        else: 

            return defer.succeed(None) 

 

    def getPerspective(self, mind, username): 

        assert username == self.user 

        return ChangePerspective(self.master, self.prefix)