~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/poppy/server.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-02 23:29:39 UTC
  • mfrom: (13582.1.3 murder-poppy)
  • Revision ID: launchpad@pqm.canonical.com-20110802232939-bn6kxbaq765v5yc7
[r=julian-edwards][no-qa] Remove the old FTP-only poppy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
 
# GNU Affero General Public License version 3 (see the file LICENSE).
3
 
 
4
 
__metaclass__ = type
5
 
 
6
 
import asyncore
7
 
import tempfile
8
 
from time import time
9
 
 
10
 
from zope.server.ftp.server import (
11
 
    FTPServerChannel,
12
 
    STORChannel as OriginalSTORChannel,
13
 
    )
14
 
from zope.server.serverbase import ServerBase
15
 
from zope.server.taskthreads import ThreadedTaskDispatcher
16
 
 
17
 
from lp.poppy.filesystem import UploadFileSystem
18
 
 
19
 
 
20
 
class Channel(FTPServerChannel):
21
 
 
22
 
    def __init__(self, server, conn, addr, adj=None):
23
 
        # Work around a zope3 bug where the status messages dict is copied by
24
 
        # reference, not by value.
25
 
        self.status_messages = dict(self.status_messages)
26
 
        self.status_messages['SERVER_READY'] = (
27
 
            '220 %s Canonical FTP server ready.')
28
 
 
29
 
        FTPServerChannel.__init__(self, server, conn, addr, adj=None)
30
 
        self.peername = self.socket.getpeername()
31
 
        self.uploadfilesystem, self.fsroot = server.newClient(self)
32
 
        self.hook = server.auth_verify_hook
33
 
 
34
 
    def close(self):
35
 
        FTPServerChannel.close(self)
36
 
        self.server.clientFinished(self)
37
 
 
38
 
    def _getFileSystem(self):
39
 
        return self.uploadfilesystem
40
 
 
41
 
    def received(self, data):
42
 
        # XXX Steve Alexander 2005-01-18
43
 
        #     This is a work-around for a bug in Zope 3's ServerChannelBase
44
 
        #     that it doesn't update self.last_activity.
45
 
        #     This method can be removed once Zope3 is fixed, and we're using
46
 
        #     that code.
47
 
        #     http://collector.zope.org/Zope3-dev/350
48
 
        self.record_activity()
49
 
        FTPServerChannel.received(self, data)
50
 
 
51
 
    def record_activity(self):
52
 
        self.last_activity = time()
53
 
 
54
 
    def cmd_pass(self, args):
55
 
        'See IFTPCommandHandler'
56
 
        self.authenticated = 0
57
 
        password = args
58
 
        credentials = (self.username, password)
59
 
        okay = True
60
 
        if self.hook:
61
 
            try:
62
 
                if not self.hook(self.fsroot, self.username, password):
63
 
                    okay = False
64
 
            except:
65
 
                okay = False
66
 
        if not okay:
67
 
            self.reply('LOGIN_MISMATCH')
68
 
            self.close_when_done()
69
 
        else:
70
 
            self.credentials = credentials
71
 
            self.authenticated = 1
72
 
            self.reply('LOGIN_SUCCESS')
73
 
 
74
 
    def cmd_stor(self, args, write_mode='w'):
75
 
        'See IFTPCommandHandler'
76
 
        if not args:
77
 
            self.reply('ERR_ARGS')
78
 
            return
79
 
        path = self._generatePath(args)
80
 
 
81
 
        start = 0
82
 
        if self.restart_position:
83
 
            self.start = self.restart_position
84
 
        mode = write_mode + self.type_mode_map[self.transfer_mode]
85
 
 
86
 
        if not self._getFileSystem().writable(path):
87
 
            self.reply('ERR_OPEN_WRITE', "Can't write file")
88
 
            return
89
 
 
90
 
        cdc = STORChannel(self, (path, mode, start))
91
 
        self.syncConnectData(cdc)
92
 
        self.reply('OPEN_CONN', (self.type_map[self.transfer_mode], path))
93
 
 
94
 
    def cmd_cwd(self, args):
95
 
        """Permissive 'cwd', creates any target directories requested.
96
 
 
97
 
        It relies on the filesystem layer to create directories recursivelly.
98
 
        """
99
 
        path = self._generatePath(args)
100
 
        if not self._getFileSystem().type(path) == 'd':
101
 
            self._getFileSystem().mkdir(path)
102
 
        self.cwd = path
103
 
        self.reply('SUCCESS_250', 'CWD')
104
 
 
105
 
 
106
 
class STORChannel(OriginalSTORChannel):
107
 
 
108
 
    def received (self, data):
109
 
        if data:
110
 
            self.inbuf.append(data)
111
 
            self.control_channel.record_activity()
112
 
            # This is the point at which some data for an upload has been
113
 
            # received by the server from a client.
114
 
 
115
 
 
116
 
class Server(ServerBase):
117
 
 
118
 
    channel_class = Channel
119
 
 
120
 
    def __init__(self, ip, port,
121
 
                 new_client_hook, client_done_hook, auth_verify_hook,
122
 
                 *args, **kw):
123
 
        ServerBase.__init__(self, ip, port, *args, **kw)
124
 
        self.new_client_hook = new_client_hook
125
 
        self.client_done_hook = client_done_hook
126
 
        self.auth_verify_hook = auth_verify_hook
127
 
 
128
 
    def newClient(self, channel):
129
 
        fsroot = tempfile.mkdtemp("-poppy")
130
 
        uploadfilesystem = UploadFileSystem(fsroot)
131
 
        clienthost, clientport = channel.peername
132
 
        try:
133
 
            self.new_client_hook(fsroot, clienthost, clientport)
134
 
        except Exception:
135
 
            # Almost bare except, result logged, to keep server running.
136
 
            self.logger.exception("Exception during new client hook")
137
 
        return uploadfilesystem, fsroot
138
 
 
139
 
    def clientFinished(self, channel):
140
 
        clienthost, clientport = channel.peername
141
 
        try:
142
 
            self.client_done_hook(channel.fsroot, clienthost, clientport)
143
 
        except Exception:
144
 
            # Almost bare except, result logged, to keep server running.
145
 
            self.logger.exception("Exception during client done hook")
146
 
 
147
 
 
148
 
def run_server(host, port, ident, numthreads,
149
 
               new_client_hook, client_done_hook, auth_verify_hook=None):
150
 
    task_dispatcher = ThreadedTaskDispatcher()
151
 
    task_dispatcher.setThreadCount(numthreads)
152
 
    server = Server(host, port,
153
 
                    new_client_hook, client_done_hook, auth_verify_hook,
154
 
                    task_dispatcher=task_dispatcher)
155
 
    server.SERVER_IDENT = ident
156
 
    try:
157
 
        asyncore.loop()
158
 
    except KeyboardInterrupt:
159
 
        # Exit without spewing an exception.
160
 
        pass