Release new version 0.8
[grml-paste.git] / grml-paste
1 #!/usr/bin/python
2 # Filename:      grml-paste
3 # Purpose:       XmlRpc interface client to paste.grml.org
4 # Author:        Michael Gebetsroither <gebi@grml.org>
5 # License:       This file is licensed under the GPL v2.
6 ################################################################################
7
8 import sys
9 import xmlrpclib
10 import optparse
11 import inspect
12 import getpass
13
14 # program defaults
15 DEFAULT_SERVER='http://paste.grml.org/server.pl'
16
17 class ActionFailedException(Exception):
18     '''Thrown if server returned an error'''
19     def __init__(self, errormsg, ret):
20         Exception.__init__(self, errormsg, ret)
21     def what(self):
22         '''Get errormessage'''
23         return self.args[0]
24     def dwhat(self):
25         '''Get more verbose errormessage'''
26         return self.args[1]
27
28
29 class Action(object):
30     def __init__(self, args, opts):
31         self.args_ = args
32         self.opts_ = opts
33
34     def _createProxy(self):
35         return xmlrpclib.ServerProxy(self.opts_.server, verbose=False)
36
37     def _callProxy(self, functor, server=None):
38         '''Wrapper for xml-rpc calls to server which throws an
39            ActionFailedException on error'''
40         if server is None:
41             server = self._createProxy()
42         ret = functor(server)
43         if ret['rc'] != 0:
44             raise ActionFailedException(ret['statusmessage'], ret)
45         return ret
46
47     def call(self, method_name):
48         '''External Interface to call the appropriate action'''
49         return self.__getattribute__(method_name)()
50
51     def actionAddPaste(self):
52         '''Add paste to the server: <1.line> <2.line> ...
53
54         default     Read paste from stdin.
55         [text]      Every argument on the commandline will be interpreted as
56                     a seperate line of paste.
57         '''
58         server = self._createProxy()
59         o = self.opts_
60         code = self.args_
61         if len(self.args_) == 0:
62             code = [ i.rstrip() for i in sys.stdin.readlines() ]
63         code = '\n'.join(code)
64         result = self._callProxy(lambda s: s.paste.addPaste(code, o.name, o.expire * 3600, o.lang, o.private),
65                             server)
66         return (result['statusmessage'], result)
67
68     def actionDelPaste(self):
69         '''Delete paste from server: <digest>
70
71         <digest>    Digest of paste you want to remove.
72         '''
73         digest = self.args_.pop(0)
74         result = self._callProxy(lambda s: s.paste.deletePaste(digest))
75         return (result['statusmessage'], result)
76
77     def actionGetPaste(self):
78         '''Get paste from server: <id>
79
80         <id>        Id of paste you want to receive.
81         '''
82         id = self.args_.pop(0)
83         result = self._callProxy(lambda s: s.paste.getPaste(id))
84         return (result['code'], result)
85
86     def actionGetLangs(self):
87         '''Get supported language highlighting types from server'''
88         result = self._callProxy(lambda s: s.paste.getLanguages())
89         return ('\n'.join(result['langs']), result)
90
91     def actionAddShortUrl(self):
92         '''Add short-URL: <url>
93
94         <url>        Short-URL to add
95         '''
96         url = self.args_.pop(0)
97         result = self._callProxy(lambda s: s.paste.addShortURL(url))
98         return (result['url'], result)
99
100     def actionGetShortUrl(self):
101         '''Resolve short-URL: <url>
102
103         <url>        Short-URL to get clicks of
104         '''
105         url = self.args_.pop(0)
106         result = self._callProxy(lambda s: s.paste.resolveShortURL(url))
107         return (result['url'], result)
108
109     def actionGetShortUrlClicks(self):
110         '''Get clicks of short-URL: <url>
111
112         <url>        Short-URL to get clicks of
113         '''
114         url = self.args_.pop(0)
115         result = self._callProxy(lambda s: s.paste.ShortURLClicks(url))
116         return (result['count'], result)
117
118     def actionHelp(self):
119         '''Print more verbose help about specific action: <action>
120
121         <action>    Topic on which you need more verbose help.
122         '''
123         if len(self.args_) < 1:
124             alias = "help"
125         else:
126             alias = self.args_.pop(0)
127
128         if alias in actions:
129             fun = actions[alias]
130             print inspect.getdoc(self.__getattribute__(fun))
131             print "\naliase: " + " ".join([i for i in actions_r[fun] if i != alias])
132         else:
133             print "Error: No such command - %s" % (alias)
134             OPT_PARSER.print_usage()
135         sys.exit(0)
136
137
138 # actionAddPaste -> [add, a]
139 actions_r = {}
140
141 # add -> actionAddPaste
142 # a   -> actionAddPaste
143 actions   = {}
144
145 # option parser
146 OPT_PARSER = None
147
148
149 ##
150 # MAIN
151 ##
152 if __name__ == "__main__":
153     action_spec = ['actionAddPaste add a',
154                    'actionDelPaste del d rm',
155                    'actionGetPaste get g',
156                    'actionGetLangs getlangs gl langs l',
157                    'actionAddShortUrl addurl',
158                    'actionGetShortUrl geturl',
159                    'actionGetShortUrlClicks getclicks',
160                    'actionHelp     help']
161     for i in action_spec:
162         aliases = i.split()
163         cmd = aliases.pop(0)
164         actions_r[cmd] = aliases
165     for (k,v) in actions_r.items():
166         for i in v:
167             actions[i] = k
168
169     usage = "usage: %prog [options] ACTION <args>\n\n" +\
170             "actions:\n" +\
171             "\n".join(["%12s\t%s" % (v[0], inspect.getdoc(getattr(Action, k)).split('\n')[0]) \
172                 for (k,v) in actions_r.items()])
173     running_user = getpass.getuser()
174     parser = optparse.OptionParser(usage=usage)
175     parser.add_option('-n', '--name', default=running_user, help="Name of poster")
176     parser.add_option('-e', '--expire', type=int, default=72, metavar='HOURS',
177             help='Time at wich paste should expire')
178     parser.add_option('-l', '--lang', default='Plain', help='Type of language to highlight')
179     parser.add_option("-p", "--private", action="count", dest="private", default=0,
180                         help='Create hidden paste'),
181     parser.add_option('-s', '--server', default=DEFAULT_SERVER,
182             help='Paste server')
183     parser.add_option('-v', '--verbose', action='count', default=0, help='More output')
184     (opts, args) = parser.parse_args()
185     OPT_PARSER = parser
186
187     if len(args) == 0:
188         parser.error('Please provide me with an action')
189     elif args[0] in actions:
190         cmd = args.pop(0)
191         action = Action(args, opts)
192         try:
193             (msg, ret) = action.call(actions[cmd])
194             if opts.verbose == 0:
195                 print msg
196             else:
197                 print ret
198         except ActionFailedException, e:
199             sys.stderr.write('Server Error: %s\n' % e.what())
200             if opts.verbose >0:
201                 print e.dwhat()
202             sys.exit(1)
203     else:
204         parser.error('Unknown action: %s' % args[0])