File Coverage

blib/lib/Net/BitTorrentSync.pm
Criterion Covered Total %
statement 23 106 21.7
branch 3 24 12.5
condition 1 9 11.1
subroutine 7 28 25.0
pod 19 20 95.0
total 53 187 28.3


line stmt bran cond sub pod time code
1             # ABSTRACT: Perl wrapper for the BitTorrent Sync API
2             package Net::BitTorrentSync;
3              
4 1     1   998 use strict;
  1         4  
  1         36  
5 1     1   7 use warnings;
  1         1  
  1         35  
6              
7             # for now, check switching to HTTP::Tiny or Hijk
8 1     1   927 use LWP::Simple;
  1         139275  
  1         10  
9 1     1   478 use JSON;
  1         1  
  1         10  
10              
11 1     1   230 use Exporter;
  1         2  
  1         2357  
12             our @ISA = 'Exporter';
13              
14             our @EXPORT = qw(
15             start_btsync
16             set_config
17             set_listened_address
18             add_folder
19             get_folders
20             remove_folder
21             get_files
22             set_file_prefs
23             get_folder_peers
24             get_secrets
25             get_folder_prefs
26             set_folder_prefs
27             get_folder_hosts
28             set_folder_hosts
29             get_prefs
30             set_prefs
31             get_os
32             get_version
33             get_speed
34             shutdown_btsync
35             );
36              
37             our $VERSION = '0.21';
38              
39             my ($config, $listen);
40              
41             =head1 NAME
42              
43             Net::BitTorrentSync - A Perl interface to the BitTorrent Sync API
44              
45             =head1 VERSION
46              
47             version 0.21
48              
49             =head1 SYNOPSIS
50              
51             use Net::BitTorrentSync;
52              
53             start_btsync('/path/to/btsync_executable', '/path/to/config_file');
54              
55             or
56              
57             set_config('/path/to/config_file');
58              
59             or
60              
61             set_listened_path('127.0.0.1:8888');
62              
63             then
64              
65             add_folder('/path/to/folder');
66              
67             my $folders = get_folders();
68              
69             remove_folder($folders->[0]->{secret});
70              
71              
72             =head1 DESCRIPTION
73              
74             BitTorrent Sync uses the BitTorrent protocol to sync files between
75             two or more machines, or nodes (computers or mobile phones) without
76             the need of a server. It uses "secrets", a unique hash string given
77             for each folder that replaces the need for a tracker machine.
78             The more nodes the network has, the faster the data will be synched
79             between the nodes, allowing for very fast exchange rates.
80             In addition, folders and files can be shared as read-only, or as
81             read and write.
82              
83             This is a complete wrapper of the published BitTorrent Sync API.
84             It can be used to connect your Perl application to a running
85             BitTorrent Sync instance, in order to perform any action such as
86             adding, removing folders/files, querying them, setting preferences,
87             and fetch information about the BitTorrent Sync instance.
88              
89             =head1 !WARNING!
90              
91             The BitTorrent Sync technology and the existing BitTorrent Sync
92             client are not open source or free software, nor are their specs
93             available in any shape or form other than the API. Therefore, there
94             is no guarantee whatsoever that the communication between nodes is
95             not being monitored by BitTorrent Inc. or by any third party,
96             including the US Government or any Agency on behalf of the US
97             Government.
98              
99             =head1 REQUIREMENTS
100              
101             In order to run these commands you must have a running instance of the
102             BitTorrent Sync client, available for download here:
103             L.
104              
105             No other non-perl requirements are needed.
106              
107             You will need an API key, for which you'll need to apply here:
108             L
109              
110             Once BitTorrent Sync is installed, either add its executable's location
111             to the system path, or pass the location of the executable and the config
112             file to the start_btsync function.
113              
114             =head1 CONFIG FILE
115              
116             To enable the API, you must run BitTorrent Sync with the config file.
117             This can be achieved either through the function start_btsync, or
118             manually:
119              
120             On Mac and Linux, run the Sync executable with --config path_to_file
121             argument.
122             On Windows, use /config path_to_file.
123              
124             The config file may be located in any directory on your drive.
125              
126             If you wish for this module to locate it automatically, you need
127             to name it btconfig and add its path to the environment path variable.
128              
129             Sync uses JSON format for the configuration file.
130             Here is a sample config file that you can use to enable API:
131              
132             {
133             // path to folder where Sync will store its internal data,
134             // folder must exist on disk
135             "storage_path" : "/Users/user/.SyncAPI",
136              
137             // run Sync in GUI-less mode
138             "use_gui" : false,
139              
140             "webui" : {
141             // IP address and port to access HTTP API
142             "listen" : "127.0.0.1:8888",
143             // login and password for HTTP basic authentication
144             // authentication is optional
145             "login" : "api",
146             "password" : "secret",
147             // API key received from BitTorrent
148             "api_key" : "xxx"
149             }
150             }
151              
152             =head1 METHODS
153              
154             =head2 start_btsync
155              
156             Launches a system command that starts the BitTorrent Sync program.
157             Returns the full path to the BitTorrent Sync executable.
158              
159             =over 4
160              
161             =item executable (required)
162              
163             Specifies path to the BitTorrent Sync executable.
164             Alternatively, you can start the process manually and call either
165             set_config or set_listened_address.
166              
167             =item config_file (required)
168              
169             Specifies path to the config file path.
170              
171             =back
172              
173             =cut
174              
175             sub start_btsync {
176 1     1 1 872 my ($btsync, $cfg_path) = @_;
177 1   33     8 $btsync ||= _find_in_path('btsync');
178 0   0     0 $cfg_path ||= _find_in_path('btconfig');
179              
180 0 0       0 if ($^O eq 'MSWin32') {
181 0         0 ($btsync, $cfg_path) = map {
182 0         0 _format_windows_path($_)
183             } ($btsync,$cfg_path);
184 0         0 system("\"$btsync\" /config \"$cfg_path\"");
185             } else {
186 0         0 system("$btsync --config $cfg_path");
187             }
188 0         0 set_config($cfg_path);
189 0         0 return $btsync;
190             }
191              
192             =head2 set_config
193              
194             Parses the config file to get the listened address from.
195             Alternatively, you can use set_listened_address.
196              
197             returns the config JSON parsed to a Perl HashRef.
198              
199             =over 4
200              
201             =item config_file (required)
202              
203             Specifies path to the config file.
204              
205             =back
206              
207             =cut
208              
209             sub set_config {
210 0     0 1 0 my $cfg_path = shift;
211 0   0     0 $cfg_path ||= _find_in_path('btconfig');
212              
213 0         0 local $/;
214 0 0       0 open my $fh, '<', $cfg_path or
215             die "Error opening config file $cfg_path - $!\n";
216 0         0 $config = decode_json(<$fh>);
217 0         0 close $fh;
218 0         0 $listen = $config->{webui}->{listen};
219 0         0 return $config;
220             }
221              
222             =head2 set_listened_address
223              
224             Sets the listened address used to communicate with the BitTorrent Sync Process
225              
226             =over 4
227              
228             =item address (required)
229              
230             Specifies address that the process listens to, address should be represented as
231             "[address]:[port]"
232              
233             =back
234              
235             =cut
236              
237             sub set_listened_address {
238 0     0 1 0 $listen = shift;
239             }
240              
241             =head2 add_folder
242              
243             Adds a folder to Sync. If a secret is not specified, it will be generated
244             automatically.
245             The folder will have to pre-exist on the disk and Sync will add it into a list
246             of syncing folders.
247             Returns '0' if no errors, error code and error message otherwise.
248              
249             =over 4
250              
251             =item dir (required)
252              
253             Specifies path to the sync folder
254              
255             =item secret (optional)
256              
257             Specifies folder secret
258              
259             =item selective_sync (optional)
260              
261             Specifies sync mode: selective - 1; all files (default) - 0
262              
263             =back
264              
265             =cut
266              
267             sub add_folder {
268 0     0 1 0 my ($dir, $secret, $selective_sync) = @_;
269 0 0       0 $dir = _format_windows_path($dir) if $^O eq 'MSWin32';
270              
271 0         0 my $request = "add_folder&dir=$dir";
272              
273 0 0       0 $request .= "&secret=$secret" if $secret;
274 0 0       0 $request .= '&selective_sync=1' if $selective_sync;
275              
276 0         0 return _access_api($request);
277             }
278              
279             =head2 get_folders
280              
281             Returns an array with folders info.
282             If a secret is specified, will return info about the folder with this secret.
283              
284             [
285             {
286             dir => "/path/to/dir/"
287             secret => "A54HDDMPN4T4BTBT7SPBWXDB7JVYZ2K6D",
288             size => 23762511569,
289             type => "read_write",
290             files => 3206,
291             error => 0,
292             indexing => 0
293             }
294             ]
295              
296             =over 4
297              
298             =item secret (optional)
299              
300             If a secret is specified, will return info about the folder with this secret
301              
302             =back
303              
304             =cut
305              
306             sub get_folders {
307 0     0 1 0 my ($secret) = @_;
308 0         0 my $request = "get_folders";
309              
310 0 0       0 $request .= "&secret=$secret" if $secret;
311              
312 0         0 return _access_api($request);
313             }
314              
315             =head2 remove_folder
316              
317             Removes folder from Sync while leaving actual folder and files on disk.
318             It will remove a folder from the Sync list of folders and does not touch any
319             files or folders on disk.
320             Returns '0' if no error, '1' if there’s no folder with specified secret.
321              
322             =over 4
323              
324             =item secret (required)
325              
326             Specifies folder secret
327              
328             =back
329              
330             =cut
331              
332             sub remove_folder {
333 0     0 1 0 my ($secret) = @_;
334 0         0 my $request = "remove_folder&secret=$secret";
335              
336 0         0 return _access_api($request);
337             }
338              
339             =head2 get_files
340              
341             Returns list of files within the specified directory.
342             If a directory is not specified, will return list of files and folders within
343             the root folder.
344             Note that the Selective Sync function is only available in the API at this time.
345              
346             [
347             {
348             name => "images",
349             state => "created",
350             type => "folder"
351             },
352             {
353             have_pieces => 1,
354             name => "index.html",
355             size => 2726,
356             state => "created",
357             total_pieces => 1,
358             type => "file",
359             download => 1 # only for selective sync folders
360             }
361             ]
362              
363             =over 4
364              
365             =item secret (required)
366              
367             =item path (optional)
368              
369             Specifies path to a subfolder of the sync folder.
370              
371             =back
372              
373             =cut
374              
375             sub get_files {
376 0     0 1 0 my ($secret, $path) = @_;
377 0         0 my $request = "get_files&secret=$secret";
378              
379 0 0       0 $request .= "&path=$path" if $path;
380              
381 0         0 return _access_api($request);
382             }
383              
384             =head2 set_file_prefs
385              
386             Selects file for download for selective sync folders.
387             Returns file information with applied preferences.
388              
389             =over 4
390              
391             =item secret (required)
392              
393             =item path (required)
394              
395             Specifies path to a subfolder of the sync folder.
396              
397             =item download (required)
398              
399             Specifies if file should be downloaded (yes - 1, no - 0)
400              
401             =back
402              
403             =cut
404              
405             sub set_file_prefs {
406 0     0 1 0 my ($secret, $path, $download) = @_;
407 0         0 my $request = "get_files&secret=$secret&path=$path&download=$download";
408              
409 0         0 return _access_api($request);
410             }
411              
412             =head2 get_folder_peers
413              
414             Returns list of peers connected to the specified folder.
415              
416             [
417             {
418             id => "ARRdk5XANMb7RmQqEDfEZE-k5aI=",
419             connection => "direct", # direct or relay
420             name => "GT-I9500",
421             synced => 0, # timestamp when last sync completed
422             download => 0,
423             upload => 22455367417
424             }
425             ]
426              
427             =over 4
428              
429             =item secret (required)
430              
431             =back
432              
433             =cut
434              
435             sub get_folder_peers {
436 0     0 1 0 my ($secret) = @_;
437 0         0 my $request = "get_folder_peers&secret=$secret";
438 0         0 return _access_api($request);
439             }
440              
441             =head2 get_secrets
442              
443             Generates read-write, read-only and encryption read-only secrets.
444             If ‘secret’ parameter is specified,
445             will return secrets available for sharing under this secret.
446             The Encryption Secret is new functionality.
447             This is a secret for a read-only peer with encrypted content
448             (the peer can sync files but can not see their content).
449             One example use is if a user wanted to backup files to an untrusted,
450             unsecure, or public location.
451             This is set to disabled by default for all users but included in the API.
452              
453             {
454             read_only => "ECK2S6MDDD7EOKKJZOQNOWDTJBEEUKGME",
455             read_write => "DPFABC4IZX33WBDRXRPPCVYA353WSC3Q6",
456             encryption => "G3PNU7KTYM63VNQZFPP3Q3GAMTPRWDEZ"
457             }
458              
459             =over 4
460              
461             =item secret (required)
462              
463             =item type (optional)
464              
465             If type = encrypted, generate secret with support of encrypted peer
466              
467             =back
468              
469             NOTE: there seems to be some contradiction in the documentation
470             wrt to secret being required.
471              
472             =cut
473              
474             sub get_secrets {
475 0     0 1 0 my ($secret, $type) = @_;
476              
477 0         0 my $request = "get_secrets";
478 0 0       0 $request .= "&secret=$secret" if $secret;
479 0 0       0 $request .= "&type=encryption" if $type;
480 0         0 return _access_api($request);
481             }
482              
483             =head2 get_folder_prefs
484              
485             Returns preferences for the specified sync folder.
486              
487             {
488             search_lan => 1,
489             use_dht => 0,
490             use_hosts => 0,
491             use_relay_server => 1,
492             use_sync_trash => 1,
493             use_tracker => 1
494             }
495              
496             =over 4
497              
498             =item secret (required)
499              
500             =back
501              
502             =cut
503              
504             sub get_folder_prefs {
505 0     0 1 0 my ($secret) = @_;
506 0         0 my $request = "get_folder_prefs&secret=$secret";
507 0         0 return _access_api($request);
508             }
509              
510             =head2 set_folder_prefs
511              
512             Sets preferences for the specified sync folder.
513             Parameters are the same as in ‘Get folder preferences’.
514             Returns current settings.
515              
516             =over 4
517              
518             =item secret (required)
519              
520             =item preferences
521              
522             A hashref containing the preferences you wish to change.
523              
524             =over 4
525              
526             =item use_dht
527              
528             =item use_hosts
529              
530             =item search_lan
531              
532             =item use_relay_server
533              
534             =item use_tracker
535              
536             =item use_sync_trash
537              
538             =back
539              
540             =back
541              
542             =cut
543              
544             sub set_folder_prefs {
545 0     0 1 0 my ($secret, $prefs) = @_;
546 0         0 my $request = "set_folder_prefs&secret=$secret";
547              
548 0         0 foreach my $pref (keys %{$prefs}) {
  0         0  
549 0         0 $request .= '&' . $pref . '=' . $prefs->{$pref};
550             }
551              
552 0         0 return _access_api($request);
553             }
554              
555             =head2 get_folder_hosts
556              
557             Returns list of predefined hosts for the folder,
558             or error code if a secret is not specified.
559              
560             {
561             hosts => [
562             "192.168.1.1:4567",
563             "example.com:8975"
564             ]
565             }
566              
567             =over 4
568              
569             =item secret (required)
570              
571             =back
572              
573             =cut
574              
575             sub get_folder_hosts {
576 0     0 1 0 my ($secret) = @_;
577 0         0 my $request = "get_folder_hosts&secret=$secret";
578 0         0 return _access_api($request);
579             }
580              
581             =head2 set_folder_hosts
582              
583             Sets one or several predefined hosts for the specified sync folder.
584             Existing list of hosts will be replaced.
585             Hosts should be added as values of the ‘host’ parameter and separated by commas.
586             Returns current hosts if set successfully, error code otherwise.
587              
588             =over 4
589              
590             =item secret (required)
591              
592             =item hosts (required)
593              
594             List of hosts, each host should be represented as “[address]:[port]”
595              
596             =back
597              
598             =cut
599              
600             sub set_folder_hosts {
601 0     0 1 0 my ($secret, $hosts) = @_;
602 0         0 my $request = "set_folder_hosts&secret=$secret&hosts=";
603              
604 0         0 $request .= join ',', @{$hosts};
  0         0  
605              
606 0         0 return _access_api($request);
607             }
608              
609             =head2 get_prefs
610              
611             Returns BitTorrent Sync preferences.
612             Contains dictionary with advanced preferences.
613             Please see Sync user guide for description of each option.
614              
615             {
616             device_name => "iMac",
617             disk_low_priority => "true",
618             download_limit => 0,
619             folder_rescan_interval => "600",
620             lan_encrypt_data => "true",
621             lan_use_tcp => "false",
622             lang => -1,
623             listening_port => 11589,
624             max_file_size_diff_for_patching => "1000",
625             max_file_size_for_versioning => "1000",
626             rate_limit_local_peers => "false",
627             send_buf_size => "5",
628             sync_max_time_diff => "600",
629             sync_trash_ttl => "30",
630             upload_limit => 0,
631             use_upnp => 0,
632             recv_buf_size => "5"
633             }
634              
635             =cut
636              
637             sub get_prefs {
638 0     0 1 0 return _access_api("get_prefs");
639             }
640              
641             =head2 set_prefs
642              
643             Sets BitTorrent Sync preferences.
644             Parameters are the same as in ‘Get preferences’.
645             Advanced preferences are set as general settings. Returns current settings.
646              
647             =over 4
648              
649             =item preferences (required)
650              
651             A hashref (see get_prefs) containing the preferences you wish to change.
652              
653             =back
654              
655             =cut
656              
657             sub set_prefs {
658 0     0 1 0 my ($secret, $prefs) = @_;
659 0         0 my $request = "set_prefs";
660              
661 0         0 foreach my $pref (keys %{$prefs}) {
  0         0  
662 0         0 $request .= '&' . $pref . '=' . $prefs->{$pref};
663             }
664              
665 0         0 return _access_api($request);
666             }
667              
668             =head2 get_os
669              
670             Returns OS name where BitTorrent Sync is running.
671              
672             {
673             os => "win32"
674             }
675              
676             =cut
677              
678             sub get_os {
679 0     0 1 0 return _access_api("get_os");
680             }
681              
682             =head2 get_version
683              
684             Returns BitTorrent Sync version.
685              
686             {
687             version => "1.2.48"
688             }
689              
690             =cut
691              
692             sub get_version {
693 0     0 1 0 return _access_api("get_version");
694             }
695              
696             =head2 get_speed
697              
698             Returns current upload and download speed.
699              
700             {
701             download => 61007,
702             upload => 0
703             }
704              
705             =cut
706              
707             sub get_speed {
708 0     0 1 0 return _access_api("get_speed");
709             }
710              
711             =head2 shutdown
712              
713             Gracefully stops Sync.
714              
715             =cut
716              
717             sub shutdown_btsync {
718 0     0 0 0 return _access_api("shutdown");
719             }
720              
721             sub _access_api {
722 0     0   0 my $request = shift;
723              
724 0         0 $request = "http://$listen/api?method=" . $request;
725              
726 0         0 my $response = get $request;
727              
728 0 0       0 die "API returned undef, check if btsync process is running\n"
729             unless $response;
730              
731 0         0 return decode_json($response);
732             }
733              
734             sub _format_windows_path {
735 0     0   0 my $path = shift;
736 0         0 $path =~ s!/|\\!\\!g;
737 0         0 return $path;
738             }
739              
740             sub _find_in_path {
741 1     1   3 my $locate = shift;
742 1         10 foreach my $dir (split ':', $ENV{'PATH'}) {
743 7 100       568 opendir my $dh, $dir or die "can't opendir $dir: $!";
744 6 50       588 if (grep { $_ eq $locate } readdir $dh) {
  870         2036  
745 0         0 closedir $dh;
746 0         0 return "$dir/$locate";
747             }
748 6         157 closedir $dh;
749             }
750             }
751              
752             =head1 TODO
753              
754             Not all methods are tested still
755              
756             There's no way to make test this without a btsync executable in the path
757             I would've liked to be able to test the module without having to force the
758             user to conform to a precondition.
759              
760             Also, the current documentation is lifted verbatim from the BitTorrent Sync
761             one, there should be some more explanation on what does what on my side.
762              
763             =head1 BUGS
764              
765             Most likely. Patches, bug reports and other ideas are welcomed.
766              
767             =head1 SEE ALSO
768              
769             L
770              
771             =head1 AUTHOR
772              
773             Erez Schatz
774              
775             =head1 LICENSE
776              
777             Copyright (c) 2014 Erez Schatz.
778             This implementation of the BitTorrent Sync API is licensed under the
779             GNU General Public License (GPL) Version 3 or later.
780              
781             The BitTorrent Sync API itself,
782             and the description text used in this module is:
783              
784             Copyright (c) 2014 BitTorrent, Inc.
785              
786             =head1 DISCLAIMER OF WARRANTY
787              
788             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
789             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
790             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
791             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
792             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
793             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
794             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
795             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
796             NECESSARY SERVICING, REPAIR, OR CORRECTION.
797              
798             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
799             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
800             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE
801             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
802             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
803             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
804             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
805             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
806             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
807             SUCH DAMAGES.
808              
809             =cut
810              
811             1;