1#!/usr/bin/env ruby
2# The MIT License
3#
4# Copyright (c) 2009 Tatsuhiro Tsujikawa
5#
6# Permission is hereby granted, free of charge, to any person obtaining a copy
7# of this software and associated documentation files (the "Software"), to deal
8# in the Software without restriction, including without limitation the rights
9# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10# copies of the Software, and to permit persons to whom the Software is
11# furnished to do so, subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be included in
14# all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22# THE SOFTWARE.
23require 'xmlrpc/client'
24require 'pp'
25require 'optparse'
26
27program_name=File.basename($0)
28options={}
29args=nil
30OptionParser.new do |opt|
31  opt.on("-d","--dir DIR"){|val| options["dir"]=val}
32  opt.on("-V","--check-integrity [BOOL]", ["true","false"]){|val|
33    options["check-integrity"]= val||"true"
34  }
35  opt.on("-c","--continue [BOOL]",["true","false"]){|val|
36    options["continue"]=val||"true"
37  }
38  opt.on("--all-proxy PROXY"){|val| options["all-proxy"]=val}
39  opt.on("--all-proxy-user USER"){|val| options["all-proxy-user"]=val}
40  opt.on("--all-proxy-passwd PASSWD"){|val| options["all-proxy-passwd"]=val}
41  opt.on("--connect-timeout SEC"){|val| options["connect-timeout"]=val}
42  opt.on("--dry-run [BOOL]",["true","false"]){|val|
43    options["dry-run"]=val||"true"
44  }
45  opt.on("--lowest-speed-limit SPEED"){|val| options["lowest-speed-limit"]=val}
46  opt.on("--max-file-not-found NUM"){|val| options["max-file-not-found"]=val}
47  opt.on("-m","--max-tries N"){|val| options["max-tries"]=val}
48  opt.on("--no-proxy DOMAINS"){|val| options["no-proxy"]=val}
49  opt.on("-o","--out FILE"){|val| options["out"]=val}
50  opt.on("--proxy-method METHOD"){|val| options["proxy-method"]=val}
51  opt.on("-R","--remote-time [BOOL]",["true","false"]){|val|
52    options["remote-time"]=val||"true"
53  }
54  opt.on("-s","--split N"){|val| options["split"]=val}
55  opt.on("-t","--timeout SEC"){|val| options["timeout"]=val}
56  opt.on("--http-auth-challenge [BOOL]",["true","false"]){|val|
57    options["http-auth-challenge"]=val||"true"
58  }
59  opt.on("--http-no-cache [BOOL]",["true","false"]){|val|
60    options["http-no-cache"]=val||"true"
61  }
62  opt.on("--http-user USER"){|val| options["http-user"]=val}
63  opt.on("--http-passwd PASSWD"){|val| options["http-passwd"]=val}
64  opt.on("--http-proxy PROXY"){|val| options["http-proxy"]=val}
65  opt.on("--http-proxy-user USER"){|val| options["http-proxy-user"]=val}
66  opt.on("--http-proxy-passwd PASSWD"){|val| options["http-proxy-passwd"]=val}
67  opt.on("--https-proxy PROXY"){|val| options["https-proxy"]=val}
68  opt.on("--https-proxy-user USER"){|val| options["https-proxy-user"]=val}
69  opt.on("--https-proxy-passwd PASSWD"){|val| options["https-proxy-passwd"]=val}
70  opt.on("--referer REFERER"){|val| options["referer"]=val}
71  opt.on("--enable-http-keep-alive [BOOL]",["true","false"]){|val|
72    options["enable-http-keep-alive"]=val||"true"
73  }
74  opt.on("--enable-http-pipelining [BOOL]",["true","false"]){|val|
75    options["enable-http-pipelining"]=val||"true"
76  }
77  opt.on("--header HEADER"){|val|
78    options["header"] = [] if options["header"] == nil
79    options["header"] << val
80  }
81  opt.on("--use-head [BOOL]",["true","false"]){|val|
82    options["use-head"]=val||"true"
83  }
84  opt.on("-U","--user-agent USERAGENT"){|val| options["user-agent"]=val}
85  opt.on("--ftp-user USER"){|val| options["ftp-user"]=val}
86  opt.on("--ftp-passwd PASSWD"){|val| options["ftp-passwd"]=val}
87  opt.on("-p","--ftp-pasv [BOOL]",["true","false"]){|val|
88    options["ftp-pasv"]=val||"true"
89  }
90  opt.on("--ftp-proxy PROXY"){|val| options["ftp-proxy"]=val}
91  opt.on("--ftp-proxy-user USER"){|val| options["ftp-proxy-user"]=val}
92  opt.on("--ftp-proxy-passwd PASSWD"){|val| options["ftp-proxy-passwd"]=val}
93  opt.on("--ftp-type TYPE"){|val| options["ftp-type"]=val}
94  opt.on("--ftp-reuse-connection [BOOL]",["true","false"]){|val|
95    options["ftp-reuse-connection"]=val||"true"
96  }
97  opt.on("-n","--no-netrc [BOOL]",["true","false"]){|val|
98    options["no-netrc"]=val||"true"
99  }
100  opt.on("--reuse-uri [BOOL]",["true","false"]){|val|
101    options["reuse-uri"]=val||"true"
102  }
103  opt.on("--select-file INDEXES"){|val| options["select-file"]=val}
104  opt.on("--bt-enable-lpd [BOOL]",["true","false"]){|val|
105    options["bt-enable-lpd"]=val||"true"
106  }
107  opt.on("--bt-external-ip IPADDRESS"){|val| options["bt-external-ip"]=val}
108  opt.on("--bt-hash-check-seed [BOOL]",["true","false"]){|val|
109    options["bt-hash-check-seed"]=val||"true"
110  }
111  opt.on("--bt-max-open-files NUM"){|val| options["bt-max-open-files"]=val}
112  opt.on("--bt-max-peers NUM"){|val| options["bt-max-peers"]=val}
113  opt.on("--bt-metadata-only [BOOL]",["true","false"]){|val|
114    options["bt-metadata-only"]=val||"true"
115  }
116  opt.on("--bt-min-crypto-level LEVEL",["plain","arc4"]){|val|
117    options["bt-min-crypto-level"]=val
118  }
119  opt.on("--bt-prioritize-piece RANGE") {|val|
120    options["bt-prioritize-piece"]=val
121  }
122  opt.on("--bt-require-crypto [BOOL]",["true","false"]){|val|
123    options["bt-require-crypto"]=val||"true"
124  }
125  opt.on("--bt-request-peer-speed-limit SPEED"){|val|
126    options["bt-request-peer-speed-limit"]=val
127  }
128  opt.on("--bt-save-metadata [BOOL]",["true","false"]){|val|
129    options["bt-save-metadata"]=val||"true"
130  }
131  opt.on("--bt-seed-unverified [BOOL]",["true","false"]){|val|
132    options["bt-seed-unverified"]=val||"true"
133  }
134  opt.on("--bt-stop-timeout SEC"){|val| options["bt-stop-timeout"]=val}
135  opt.on("--bt-tracker-interval SEC"){|val| options["bt-tracker-interval"]=val}
136  opt.on("--bt-tracker-timeout SEC"){|val| options["bt-tracker-timeout"]=val}
137  opt.on("--bt-tracker-connect-timeout SEC"){|val|
138    options["bt-tracker-connect-timeout"]=val
139  }
140  opt.on("--enable-peer-exchange [BOOL]",["true","false"]){|val|
141    options["enable-peer-exchange"]=val||"true"
142  }
143  opt.on("--follow-torrent VALUE", ["true","false","mem"]){|val|
144    options["follow-torrent"]=val
145  }
146  opt.on("-O","--index-out INDEXPATH"){|val|
147    options["index-out"]=[] if options["index-out"] == nil
148    options["index-out"] << val
149  }
150  opt.on("-u","--max-upload-limit SPEED"){|val| options["max-upload-limit"]=val}
151  opt.on("--seed-ratio RATIO"){|val| options["seed-ratio"]=val}
152  opt.on("--seed-time MINUTES"){|val| options["seed-time"]=val}
153  opt.on("--follow-metalink VALUE", ["true","false","mem"]){|val|
154    options["follow-metalink"]=val
155  }
156  opt.on("-C","--metalink-servers NUM"){|val| options["metalink-servers"]=val}
157  opt.on("--metalink-language LANG"){|val| options["metalink-language"]=val}
158  opt.on("--metalink-location LOCS"){|val| options["metalink-location"]=val}
159  opt.on("--metalink-os OS"){|val| options["metalink-os"]=val}
160  opt.on("--metalink-version VERSION"){|val| options["metalink-version"]=val}
161  opt.on("--metalink-preferred-protocol PROTO"){|val|
162    options["metalink-preferred-protocol"]=val
163  }
164  opt.on("--metalink-enable-unique-protocol [BOOL]",["true","false"]){|val|
165    options["metalink-enable-unique-protocol"]=val||"true"
166  }
167  opt.on("--allow-overwrite [BOOL]",["true","false"]){|val|
168    options["allow-overwrite"]=val||"true"
169  }
170  opt.on("--allow-piece-length-change [BOOL]",["true","false"]){|val|
171    options["allow-piece-length-change"]=val||"true"
172  }
173  opt.on("--async-dns [BOOL]",["true","false"]){|val|
174    options["async-dns"]=val||"true"
175  }
176  opt.on("--auto-file-renaming [BOOL]",["true","false"]){|val|
177    options["auto-file-renaming"]=val||"true"
178  }
179  opt.on("--file-allocation METHOD",["none","prealloc","falloc"]){|val|
180    options["file-allocation"]=val
181  }
182  opt.on("--max-download-limit LIMIT"){|val| options["max-download-limit"]=val}
183  opt.on("--no-file-allocation-limit SIZE"){|val|
184    options["no-file-allocation-limit"]=val
185  }
186  opt.on("-P","--parameterized-uri [BOOL]",["true","false"]){|val|
187    options["parameterized-uri"]=val||"true"
188  }
189  opt.on("--realtime-chunk-checksum [BOOL]",["true","false"]){|val|
190    options["realtime-chunk-checksum"]=val||"true"
191  }
192  opt.on("--remove-control-file [BOOL]",["true","false"]){|val|
193    options["remove-control-file"]=val||"true"
194  }
195  opt.on("--always-resume [BOOL]",["true","false"]){|val|
196    options["always-resume"]=val||"true"
197  }
198  opt.on("--max-resume-failure-tries N"){|val|
199    options["max-resume-failure-tries"]=val
200  }
201  opt.on("--http-accept-gzip [BOOL]",["true","false"]){|val|
202    options["http-accept-gzip"]=val||"true"
203  }
204  opt.on("-x","--max-connection-per-server NUM"){|val| options["max-connection-per-server"]=val}
205  opt.on("-k","--min-split-size SIZE"){|val| options["min-split-size"]=val}
206  opt.on("--conditional-get [BOOL]",["true","false"]){|val|
207    options["conditional-get"]=val||"true"
208  }
209  opt.on("--enable-async-dns6 [BOOL]",["true","false"]){|val|
210    options["enable-async-dns6"]=val||"true"
211  }
212  opt.on("--bt-tracker URIS"){|val| options["bt-tracker"]=val}
213  opt.on("--bt-exclude-tracker URIS"){|val| options["bt-exclude-tracker"]=val}
214  opt.on("--retry-wait SEC"){|val| options["retry-wait"]=val}
215  opt.on("--metalink-base-uri URI"){|val| options["metalink-base-uri"]=val}
216  opt.on("--pause [BOOL]",["true","false"]){|val| options["pause"]=val||"true"}
217  opt.on("--stream-piece-selector SELECTOR"){|val| options["stream-piece-selector"]=val}
218  opt.on("--hash-check-only [BOOL]",["true","false"]){|val|
219    options["hash-check-only"]=val||"true"
220  }
221  opt.on("--checksum TYPE_DIGEST"){|val| options["checksum"]=val}
222  opt.on("--piece-length LENGTH"){|val| options["piece-length"]=val}
223  opt.on("--uri-selector SELECTOR"){|val| options["uri-selector"]=val}
224
225  opt.on("--max-overall-download-limit LIMIT"){|val| options["max-overall-download-limit"]=val}
226  opt.on("--max-overall-upload-limit LIMIT"){|val| options["max-overall-upload-limit"]=val}
227  opt.on("-j","--max-concurrent-downloads N"){|val| options["max-concurrent-downloads"]=val}
228  opt.on("-l","--log FILE"){|val| options["log"]=val}
229  opt.on("--max-download-result NUM"){|val| options["max-download-result"]=val}
230  opt.on("--download-result OPT"){|val| options["download-result"]=val}
231  opt.on("--keep-unfinished-download-result [BOOL]",["true","false"]){|val|
232    options["keep-unfinished-download-result"]=val||"true"
233  }
234  opt.on("--save-session FILE"){|val| options["save-session"]=val}
235  opt.on("--server-stat-of FILE"){|val| options["server-stat-of"]=val}
236  opt.on("--save-cookies FILE"){|val| options["save-cookies"]=val}
237  opt.on("--gid GID"){|val| options["gid"]=val}
238  opt.on("--pause-metadata [BOOL]",["true","false"]){|val| options["pause-metadata"]=val||"true"}
239
240  opt.on("--server SERVER", "hostname of XML-RPC server. Default: localhost"){|val| options["server"]=val }
241  opt.on("--port PORT", "port of XML-RPC server. Default: 6800"){|val| options["port"]=val }
242
243  opt.on("--user USERNAME", "XML-RPC username"){|val| options["user"]=val }
244  opt.on("--passwd PASSWORD", "XML-RPC password"){|val| options["passwd"]=val }
245  opt.on("--secure [BOOL]",["true","false"]){|val| options["secure"]=val||"true" }
246  opt.on("--check-rpc-cert [BOOL]",["true","false"]){|val| options["check-rpc-cert"]=val||"true" }
247
248  opt.on("--secret SECRET", "XML-RPC secret authorization token"){|val| options["secret"]=val }
249
250  opt.banner=<<EOS
251Usage: #{program_name} addUri URI... [options]
252       #{program_name} addTorrent /path/to/torrent_file URI... [options]
253       #{program_name} addMetalink /path/to/metalink_file [options]
254       #{program_name} remove GID [options]
255       #{program_name} forceRemove GID [options]
256       #{program_name} pause GID [options]
257       #{program_name} pauseAll [options]
258       #{program_name} forcePause GID [options]
259       #{program_name} forcePauseAll [options]
260       #{program_name} unpause GID [options]
261       #{program_name} unpauseAll [options]
262       #{program_name} changePosition GID pos how [options]
263       #{program_name} tellStatus GID [keys] [options]
264       #{program_name} tellActive [keys] [options]
265       #{program_name} tellWaiting offset num [keys] [options]
266       #{program_name} tellStopped offset num [keys] [options]
267       #{program_name} getOption GID [options]
268       #{program_name} getGlobalOption [options]
269       #{program_name} getFiles   GID [options]
270       #{program_name} getUris    GID [options]
271       #{program_name} getPeers   GID [options]
272       #{program_name} getServers GID [options]
273       #{program_name} purgeDownloadResult [options]
274       #{program_name} removeDownloadResult GID [options]
275       #{program_name} changeOption GID [options]
276       #{program_name} changeGlobalOption [options]
277       #{program_name} getVersion [options]
278       #{program_name} getSessionInfo [options]
279       #{program_name} shutdown [options]
280       #{program_name} forceShutdown [options]
281       #{program_name} getGlobalStat [options]
282       #{program_name} saveSession [options]
283       #{program_name} appendUri GID fileIndex URI... [options]
284           This command calls aria2.changeUri(GID, fileIndex, [], [URI,...])
285           internally.
286Options:
287EOS
288
289
290  args=opt.parse(ARGV)
291
292end
293
294if !args or args.size == 0 then
295  puts "No command specified"
296  exit 1
297end
298
299command=args[0]
300resources=args[1..-1]
301
302auth=""
303if options.has_key?("user") then
304  auth=options["user"]+":"+options["passwd"]+"@"
305end
306if not options.has_key?("server") then
307  options["server"]="localhost"
308end
309if not options.has_key?("port") then
310  options["port"]="6800"
311end
312if not options.has_key?("secure") then
313  options["secure"]="false"
314end
315if not options.has_key?("check-rpc-cert") then
316  options["check-rpc-cert"]="true"
317end
318secret = if options.has_key?("secret") then "token:"+options["secret"] else nil end
319
320client=XMLRPC::Client.new3({:host => options["server"],
321                             :port => options["port"],
322                             :path => "/rpc",
323                             :user => options["user"],
324                             :password => options["passwd"],
325                             :use_ssl => options["secure"] == "true"})
326
327if options["check-rpc-cert"] == "false" then
328  client.instance_variable_get(:@http).instance_variable_set(:@verify_mode, OpenSSL::SSL::VERIFY_NONE)
329end
330
331options.delete("server")
332options.delete("port")
333options.delete("user")
334options.delete("passwd")
335options.delete("secret")
336options.delete("secure")
337options.delete("check-rpc-cert")
338
339def client_call client, secret, method, *params
340  if secret.nil?
341    client.call(method, *params)
342  else
343    client.call(method, secret, *params)
344  end
345end
346
347if command == "addUri" then
348  result=client_call(client, secret, "aria2."+command, resources, options)
349elsif command == "addTorrent" then
350  torrentData=IO.read(resources[0])
351  result=client_call(client, secret, "aria2."+command,
352                     XMLRPC::Base64.new(torrentData), resources[1..-1], options)
353elsif command == "addMetalink" then
354  metalinkData=IO.read(resources[0])
355  result=client_call(client, secret, "aria2."+command,
356                     XMLRPC::Base64.new(metalinkData), options)
357elsif command == "tellStatus" then
358  result=client_call(client, secret, "aria2."+command,
359                     resources[0], resources[1..-1])
360elsif command == "tellActive" then
361  result=client_call(client, secret, "aria2."+command, resources[0..-1])
362elsif command == "tellWaiting" then
363  result=client_call(client, secret, "aria2."+command, resources[0].to_i(),
364                     resources[1].to_i(), resources[2..-1])
365elsif command == "tellStopped" then
366  result=client_call(client, secret, "aria2."+command, resources[0].to_i(),
367                     resources[1].to_i(), resources[2..-1])
368elsif command == "getOption" then
369  result=client_call(client, secret, "aria2."+command, resources[0])
370elsif command == "getGlobalOption" then
371  result=client_call(client, secret, "aria2."+command)
372elsif command == "pause" then
373  result=client_call(client, secret, "aria2."+command, resources[0])
374elsif command == "pauseAll" then
375  result=client_call(client, secret, "aria2."+command)
376elsif command == "forcePause" then
377  result=client_call(client, secret, "aria2."+command, resources[0])
378elsif command == "forcePauseAll" then
379  result=client_call(client, secret, "aria2."+command)
380elsif command == "unpause" then
381  result=client_call(client, secret, "aria2."+command, resources[0])
382elsif command == "unpauseAll" then
383  result=client_call(client, secret, "aria2."+command)
384elsif command == "remove" then
385  result=client_call(client, secret, "aria2."+command, resources[0])
386elsif command == "forceRemove" then
387  result=client_call(client, secret, "aria2."+command, resources[0])
388elsif command == "changePosition" then
389  result=client_call(client, secret, "aria2."+command, resources[0],
390                     resources[1].to_i(), resources[2])
391elsif command == "getFiles" then
392  result=client_call(client, secret, "aria2."+command, resources[0])
393elsif command == "getUris" then
394  result=client_call(client, secret, "aria2."+command, resources[0])
395elsif command == "getPeers" then
396  result=client_call(client, secret, "aria2."+command, resources[0])
397elsif command == "getServers" then
398  result=client_call(client, secret, "aria2."+command, resources[0])
399elsif command == "purgeDownloadResult" then
400  result=client_call(client, secret, "aria2."+command)
401elsif command == "removeDownloadResult" then
402  result=client_call(client, secret, "aria2."+command, resources[0])
403elsif command == "changeOption" then
404  result=client_call(client, secret, "aria2."+command, resources[0], options)
405elsif command == "changeGlobalOption" then
406  result=client_call(client, secret, "aria2."+command, options)
407elsif command == "getVersion" then
408  result=client_call(client, secret, "aria2."+command)
409elsif command == "getSessionInfo" then
410  result=client_call(client, secret, "aria2."+command)
411elsif command == "shutdown" then
412  result=client_call(client, secret, "aria2."+command)
413elsif command == "forceShutdown" then
414  result=client_call(client, secret, "aria2."+command)
415elsif command == "getGlobalStat" then
416  result=client_call(client, secret, "aria2."+command)
417elsif command == "saveSession" then
418  result=client_call(client, secret, "aria2."+command)
419elsif command == "appendUri" then
420  result=client_call(client, secret, "aria2.changeUri", resources[0],
421                     resources[1].to_i(), [], resources[2..-1])
422else
423  puts "Command not recognized"
424  exit 1
425end
426
427pp result
428