Release new version 0.8
[grml-paste.git] / grml-paste
1 #!/usr/bin/python3
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 getpass
9 import inspect
10 import optparse
11 import sys
12 from xmlrpc.client import ServerProxy
13
14 # program defaults
15 DEFAULT_SERVER = "https://paste.grml.org/server.pl"
16
17
18 class ActionFailedException(Exception):
19     """Thrown if server returned an error"""
20
21     def __init__(self, errormsg, ret):
22         Exception.__init__(self, errormsg, ret)
23
24     def what(self):
25         """Get errormessage"""
26         return self.args[0]
27
28     def dwhat(self):
29         """Get more verbose errormessage"""
30         return self.args[1]
31
32
33 def _paste(opts):
34     """Get paste proxy object"""
35     return ServerProxy(opts.server, verbose=False).paste
36
37
38 def _check_success(return_data):
39     """Check if call was successful, raise ActionFailedException otherwise"""
40     if return_data["rc"] != 0:
41         raise ActionFailedException(return_data["statusmessage"], return_data)
42     return return_data
43
44
45 def action_add_paste(opts, code):
46     """Add paste to the server: <1.line> <2.line> ...
47
48     default     Read paste from stdin.
49     [text]      Every argument on the commandline will be interpreted as
50                 a seperate line of paste.
51     """
52     if len(code) == 0:
53         code = [line.rstrip() for line in sys.stdin.readlines()]
54     code = "\n".join(code)
55     result = _check_success(
56         _paste(opts).addPaste(
57             code, opts.name, opts.expire * 3600, opts.lang, opts.private
58         )
59     )
60     return result["statusmessage"], result
61
62
63 def action_del_paste(opts, args):
64     """Delete paste from server: <digest>
65
66     <digest>    Digest of paste you want to remove.
67     """
68     digest = args.pop(0)
69     result = _check_success(_paste(opts).deletePaste(digest))
70     return result["statusmessage"], result
71
72
73 def action_get_paste(opts, args):
74     """Get paste from server: <id>
75
76     <id>        Id of paste you want to receive.
77     """
78     paste_id = args.pop(0)
79     result = _check_success(_paste(opts).getPaste(paste_id))
80     return result["code"], result
81
82
83 def action_get_langs(opts, args):
84     """Get supported language highlighting types from server"""
85     result = _check_success(_paste(opts).getLanguages())
86     return "\n".join(result["langs"]), result
87
88
89 def action_add_short_url(opts, args):
90     """Add short-URL: <url>
91
92     <url>        Short-URL to add
93     """
94     url = args.pop(0)
95     result = _check_success(_paste(opts).addShortURL(url))
96     return result["url"], result
97
98
99 def action_get_short_url(opts, args):
100     """Resolve short-URL: <url>
101
102     <url>        Short-URL to get clicks of
103     """
104     url = args.pop(0)
105     result = _check_success(_paste(opts).resolveShortURL(url))
106     return result["url"], result
107
108
109 def action_get_short_url_clicks(opts, args):
110     """Get clicks of short-URL: <url>
111
112     <url>        Short-URL to get clicks of
113     """
114     url = args.pop(0)
115     result = _check_success(_paste(opts).ShortURLClicks(url))
116     return result["count"], result
117
118
119 def action_help(opts, args):
120     """Print more verbose help about specific action: <action>
121
122     <action>    Topic on which you need more verbose help.
123     """
124     if len(args) < 1:
125         alias = "help"
126     else:
127         alias = args.pop(0)
128
129     if alias in actions:
130         function_name = actions[alias]
131         print(inspect.getdoc(globals()[function_name]))
132         print(
133             "\naliases: "
134             + " ".join([i for i in actions_r[function_name] if i != alias])
135         )
136     else:
137         print("Error: No such command - %s" % (alias))
138         OPT_PARSER.print_usage()
139     sys.exit(0)
140
141
142 # action_add_paste -> [add, a]
143 actions_r = {}
144
145 # add -> action_add_paste
146 # a   -> action_add_paste
147 actions = {}
148
149 # option parser
150 OPT_PARSER = None
151
152
153 ##
154 # MAIN
155 ##
156 if __name__ == "__main__":
157     action_specs = [
158         "action_add_paste add a",
159         "action_del_paste del d rm",
160         "action_get_paste get g",
161         "action_get_langs getlangs gl langs l",
162         "action_add_short_url addurl",
163         "action_get_short_url geturl",
164         "action_get_short_url_clicks getclicks",
165         "action_help     help",
166     ]
167     for action_spec in action_specs:
168         aliases = action_spec.split()
169         cmd = aliases.pop(0)
170         actions_r[cmd] = aliases
171     for (action_name, v) in actions_r.items():
172         for i in v:
173             actions[i] = action_name
174
175     usage = (
176         "usage: %prog [options] ACTION <args>\n\n"
177         + "actions:\n"
178         + "\n".join(
179             [
180                 "%12s\t%s"
181                 % (v[0], inspect.getdoc(globals()[action_name]).splitlines()[0])
182                 for (action_name, v) in actions_r.items()
183             ]
184         )
185     )
186     running_user = getpass.getuser()
187     parser = optparse.OptionParser(usage=usage)
188     parser.add_option("-n", "--name", default=running_user, help="Name of poster")
189     parser.add_option(
190         "-e",
191         "--expire",
192         type=int,
193         default=72,
194         metavar="HOURS",
195         help="Time at which paste should expire",
196     )
197     parser.add_option(
198         "-l", "--lang", default="Plain", help="Type of language to highlight"
199     )
200     parser.add_option(
201         "-p",
202         "--private",
203         action="count",
204         dest="private",
205         default=0,
206         help="Create hidden paste",
207     ),
208     parser.add_option("-s", "--server", default=DEFAULT_SERVER, help="Paste server")
209     parser.add_option("-v", "--verbose", action="count", default=0, help="More output")
210     (opts, args) = parser.parse_args()
211     OPT_PARSER = parser
212
213     if len(args) == 0:
214         parser.error("Please provide me with an action")
215     elif args[0] in actions:
216         cmd = args.pop(0)
217         action = actions[cmd]
218         try:
219             (msg, ret) = globals()[action](opts, args)
220             if opts.verbose == 0:
221                 print(msg)
222             else:
223                 print(ret)
224         except ActionFailedException as except_inst:
225             sys.stderr.write("Server Error: %s\n" % except_inst.what())
226             if opts.verbose > 0:
227                 print(except_inst.dwhat())
228             sys.exit(1)
229     else:
230         parser.error("Unknown action: %s" % args[0])