]> www.wagner.pp.ru Git - oss/ljdump.git/blob - ljdump.py
67e185155139820523b3beeaa952d3a10b5a4560
[oss/ljdump.git] / ljdump.py
1 #
2 # ljdump.py - livejournal archiver
3 # Greg Hewgill <greg@hewgill.com> http://hewgill.com
4 # Version 1.0
5 #
6 # $Id$
7 #
8 # This program reads the journal entries from a livejournal (or compatible)
9 # blog site and archives them in a subdirectory named after the journal name.
10 #
11 # The configuration is read from "ljdump.config". A sample configuration is
12 # provided in "ljdump.config.sample", which should be copied and then edited.
13 # The configuration settings are:
14 #
15 #   server - The XMLRPC server URL. This should only need to be changed
16 #            if you are dumping a journal that is livejournal-compatible
17 #            but is not livejournal itself.
18 #
19 #   username - The livejournal user name. A subdirectory will be created
20 #              with this same name to store the journal entries.
21 #
22 #   password - The account password. This password is never sent in the
23 #              clear; the livejournal "challenge" password mechanism is used.
24 #
25 # This program may be run as often as needed to bring the backup copy up
26 # to date. Only new items are downloaded.
27 #
28 # LICENSE
29 #
30 # This software is provided 'as-is', without any express or implied
31 # warranty.  In no event will the author be held liable for any damages
32 # arising from the use of this software.
33 #
34 # Permission is granted to anyone to use this software for any purpose,
35 # including commercial applications, and to alter it and redistribute it
36 # freely, subject to the following restrictions:
37 #
38 # 1. The origin of this software must not be misrepresented; you must not
39 #    claim that you wrote the original software. If you use this software
40 #    in a product, an acknowledgment in the product documentation would be
41 #    appreciated but is not required.
42 # 2. Altered source versions must be plainly marked as such, and must not be
43 #    misrepresented as being the original software.
44 # 3. This notice may not be removed or altered from any source distribution.
45 #
46 # Copyright (c) 2005 Greg Hewgill
47
48 import codecs, md5, os, pprint, sys, xml.dom.minidom, xmlrpclib
49 from xml.sax import saxutils
50
51 def dochallenge(params, password):
52     challenge = server.LJ.XMLRPC.getchallenge()
53     params.update({
54         'auth_method': "challenge",
55         'auth_challenge': challenge['challenge'],
56         'auth_response': md5.new(challenge['challenge']+md5.new(password).hexdigest()).hexdigest()
57     })
58     return params
59
60 def dumpelement(f, name, e):
61     f.write("<%s>\n" % name)
62     for k in e.keys():
63         if isinstance(e[k], {}.__class__):
64             dumpelement(f, k, e[k])
65         else:
66             s = unicode(str(e[k]), "UTF-8")
67             f.write("<%s>%s</%s>\n" % (k, saxutils.escape(s), k))
68     f.write("</%s>\n" % name)
69
70 def writedump(fn, event):
71     f = codecs.open(fn, "w", "UTF-8")
72     f.write("""<?xml version="1.0"?>\n""")
73     dumpelement(f, "event", event)
74     f.close()
75
76 config = xml.dom.minidom.parse("ljdump.config")
77 Server = config.documentElement.getElementsByTagName("server")[0].childNodes[0].data
78 Username = config.documentElement.getElementsByTagName("username")[0].childNodes[0].data
79 Password = config.documentElement.getElementsByTagName("password")[0].childNodes[0].data
80
81 print "Fetching journal entries for: %s" % Username
82 try:
83     os.mkdir(Username)
84     print "Created subdirectory: %s" % Username
85 except:
86     pass
87
88 server = xmlrpclib.ServerProxy(Server)
89
90 total = 0
91 fetched = 0
92 errors = 0
93
94 last = ""
95 while True:
96     r = server.LJ.XMLRPC.syncitems(dochallenge({
97         'username': Username,
98         'ver': 1,
99         'lastsync': last,
100     }, Password))
101     #pprint.pprint(r)
102     if len(r['syncitems']) == 0:
103         break
104     for item in r['syncitems']:
105         if item['item'][0] == 'L':
106             fn = "%s/%s" % (Username, item['item'])
107             if not os.access(fn, os.F_OK):
108                 print "Fetching journal entry %s" % item['item']
109                 try:
110                     e = server.LJ.XMLRPC.getevents(dochallenge({
111                         'username': Username,
112                         'ver': 1,
113                         'selecttype': "one",
114                         'itemid': item['item'][2:],
115                     }, Password))
116                     writedump(fn, e['events'][0])
117                     fetched += 1
118                 except xmlrpclib.Fault, x:
119                     print "Error getting item: %s" % item['item']
120                     pprint.pprint(x)
121                     errors += 1
122         last = item['time']
123         total += 1
124 print "%d total entries" % total
125 print "%d fetched entries" % fetched
126 if errors > 0:
127     print "%d errors" % errors