Browse Source

Initial import

Working features:
- login
- logout
- list
- upload
Thomas Jost 12 years ago
commit
d025811823
3 changed files with 209 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 207
    0
      FileDropper.py
  3. 1
    0
      test.txt

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+.*.swp

+ 207
- 0
FileDropper.py View File

@@ -0,0 +1,207 @@
1
+#!/usr/bin/env python
2
+# -*- coding: utf-8 -*-
3
+
4
+"""Interact with FileDropper.com"""
5
+
6
+import os.path, re, urllib, urllib2
7
+from BeautifulSoup import BeautifulSoup
8
+import poster.encode, poster.streaminghttp
9
+
10
+# Permissions
11
+FD_PERM_PUBLIC   = 0
12
+FD_PERM_PASSWORD = 1
13
+FD_PERM_PRIVATE  = 2
14
+
15
+# Prefix for all the URLs used in the module
16
+FD_URL         = "http://www.filedropper.com/"
17
+FD_LOGIN_URL   = FD_URL + "login.php"
18
+FD_PREMIUM_URL = FD_URL + "premium.php"
19
+FD_UPLOAD_URL  = FD_URL + "index.php?xml=true"
20
+
21
+# Max allowed size
22
+FD_MAX_SIZE = 5*(1024**3)
23
+
24
+# Error codes
25
+FD_ERROR_NOT_LOGGED_IN = 1
26
+FD_ERROR_PERMISSIONS   = 2
27
+FD_ERROR_FILE_TOO_BIG  = 3
28
+FD_ERROR_UPLOAD        = 4
29
+FD_ERRORS = {
30
+    FD_ERROR_NOT_LOGGED_IN: "Not logged in",
31
+    FD_ERROR_PERMISSIONS  : "Invalid permissions",
32
+    FD_ERROR_FILE_TOO_BIG : "File is too big",
33
+    FD_ERROR_UPLOAD       : "Error while uploading a file",
34
+}
35
+
36
+# Regexp used for parsing HTML pages
37
+FD_RE_LIST = re.compile("^unhide\('(\d+)'\)")
38
+
39
+class FileDropperException(Exception):
40
+    def __init__(self, errno):
41
+        self.errno = errno
42
+
43
+    def __str__(self):
44
+        pass
45
+
46
+class FileDropper:
47
+    """Builds an empty FileDropper object that may be used for uploading files
48
+    to FileDropper.com, get details about files in a premium account, change
49
+    their permission or delete them."""
50
+
51
+    def __init__(self):
52
+        self.logged_in = False
53
+
54
+        # Init the streaming HTTP handler
55
+        #register_openers()
56
+
57
+        # Init the URL opener
58
+        #self.url = urllib2.build_opener(urllib2.HTTPCookieProcessor())
59
+        self.url = urllib2.build_opener(
60
+                poster.streaminghttp.StreamingHTTPHandler,
61
+                poster.streaminghttp.StreamingHTTPRedirectHandler,
62
+                urllib2.HTTPCookieProcessor
63
+        )
64
+
65
+    def __del__(self):
66
+        if self.logged_in:
67
+            self.logout()
68
+
69
+    def login(self, username, password):
70
+        """Log into the premium account with the given username and password."""
71
+
72
+        # Build the data string to send with the POST request
73
+        data = urllib.urlencode({"username": username, "password": password})
74
+
75
+        # Send the request
76
+        res = self.url.open(FD_LOGIN_URL, data)
77
+
78
+        # What is our final URL?
79
+        dst_url = res.geturl()
80
+
81
+        self.logged_in = (res.getcode() == 200) and (dst_url == FD_PREMIUM_URL)
82
+        return self.logged_in
83
+
84
+    def logout(self):
85
+        """Log out from a premium account."""
86
+
87
+        if not self.logged_in:
88
+            raise FileDropperException(FD_ERROR_NOT_LOGGED_IN)
89
+
90
+        self.url.open(FD_URL + "login.php?action=logout")
91
+
92
+    def list(self):
93
+        """Get a list of files in the file manager of a premium account.
94
+
95
+        The return value is a list of 7-value tuples of the form
96
+        (file_name, id, downloads, size, date, permissions, public_url)"""
97
+
98
+        if not self.logged_in:
99
+            raise FileDropperException(FD_ERROR_NOT_LOGGED_IN)
100
+
101
+        # Download the page
102
+        html = self.url.open(FD_PREMIUM_URL).read()
103
+
104
+        # Parse it
105
+        soup = BeautifulSoup(html)
106
+
107
+        # Get files info
108
+        tags = [tag.parent for tag in soup.findAll("a", onclick = FD_RE_LIST)]
109
+        files = []
110
+        for tag in tags:
111
+            # File name in the link
112
+            file_name = tag.a.string.strip()
113
+
114
+            # File ID found using the regexp
115
+            m = FD_RE_LIST.search(tag.a['onclick'])
116
+            file_id = int(m.group(1))
117
+
118
+            div = tag.div
119
+
120
+            # Some dirty searches in an ugly div section
121
+            downloads = int(div.contents[2].replace('|', '').strip())
122
+            size = div.contents[4].replace('|', '').strip()      #TODO: parse it correctly
123
+            date = div.contents[6].replace(' ', '').strip() #TODO: parse it correctly
124
+
125
+            # Permissions: conversion from 2 strings to a symbol
126
+            raw_perm = div.find("span", id="fileperms[%d]" % file_id)
127
+            permissions = -1
128
+            if raw_perm.span.string == "Private":
129
+                permissions = FD_PERM_PRIVATE
130
+            elif raw_perm.span.string == "Public":
131
+                # If there is a <b> tag, it contains "No password"
132
+                if raw_perm.b is not None:
133
+                    permissions = FD_PERM_PUBLIC
134
+                else:
135
+                    permissions = FD_PERM_PASSWORD
136
+            else:
137
+                raise FileDropperException(FD_ERROR_PERMISSIONS)
138
+
139
+            # Public URL (may be published safely anywhere)
140
+            public_url = div.find("input", type="text")['value']
141
+
142
+            value = (file_name, file_id, downloads, size, date, permissions, public_url)
143
+            files.append(value)
144
+
145
+        return files
146
+
147
+    def upload(self, filename):
148
+        """Upload the specified file"""
149
+
150
+        # Check the file size
151
+        if os.path.getsize(filename) > FD_MAX_SIZE:
152
+            raise FileDropperException(FD_ERROR_FILE_TOO_BIG)
153
+
154
+        # Prepare the encoded data
155
+        base_name = os.path.basename(filename)
156
+
157
+        mp1 = poster.encode.MultipartParam("Filename", base_name)
158
+        mp2 = poster.encode.MultipartParam("file", filename=base_name, filetype="application/octet-stream", fileobj=open(filename))
159
+
160
+        data, headers = poster.encode.multipart_encode([mp1, mp2])
161
+
162
+        # Prepare the request
163
+        req = urllib2.Request(FD_UPLOAD_URL, data, headers)
164
+
165
+        # Send the request
166
+        res = self.url.open(req)
167
+
168
+        # Get the intermediate url
169
+        tmp_url = res.read()
170
+        #TODO: check if upload failed...
171
+
172
+        # Get the real file URL... and end with a 404 error :)
173
+        try:
174
+            res = self.url.open(FD_URL + tmp_url[1:])
175
+        except urllib2.HTTPError, exc:
176
+            if exc.code == 404:
177
+                return exc.geturl()
178
+            else:
179
+                raise exc
180
+
181
+        # We should not reach this point as there is supposed to be a 404 error
182
+        raise FileDropperException(FD_ERROR_UPLOAD)
183
+
184
+    def set_perm(self, file_id, perm):
185
+        pass
186
+
187
+    def delete(self, file_id):
188
+        pass
189
+
190
+
191
+if __name__ == "__main__":
192
+    from getpass import getpass
193
+    fd = FileDropper()
194
+    user = raw_input("Username: ")
195
+    if user != "":
196
+        password = getpass()
197
+        if not fd.login(user, password):
198
+            print "Login failed"
199
+
200
+    print "Current files:"
201
+    print fd.list()
202
+    print
203
+    uploaded_file = fd.upload("test.txt")
204
+    print "Upload: %s" % uploaded_file
205
+    print
206
+    print "New files:"
207
+    print fd.list()

+ 1
- 0
test.txt View File

@@ -0,0 +1 @@
1
+Hello, world :)

Loading…
Cancel
Save