2e873c9f74ec929b328ef3e99b18e1a9a31756f8
[cinaest] / src / imdb / ftp-downloader.vala
1 class FtpDownloader {
2         private Curl.EasyHandle curl;
3         private Cancellable cancellable;
4         private FileStream file;
5         private string dirname;
6         private HashTable<string,int> file_size;
7
8         public FtpDownloader (Cancellable? _cancellable) {
9                 cancellable = _cancellable;
10                 curl = new Curl.EasyHandle ();
11         }
12
13         [CCode (instance_pos = -1)]
14         size_t write_callback (void *buffer, size_t size, size_t nmemb) {
15                 if (cancellable != null && cancellable.is_cancelled ())
16                         return 0;
17
18                 unowned uint8[] buf = (uint8[]) buffer;
19                 buf.length = (int) (size * nmemb);
20
21                 file.write (buf);
22
23                 return buf.length;
24         }
25
26         private int last_dlnow;
27
28         int progress_callback (double dltotal, double dlnow, double ultotal, double ulnow) {
29                 if (cancellable != null && cancellable.is_cancelled ())
30                         return 1;
31                 if (last_dlnow != (int) dlnow) {
32                         last_dlnow = (int) dlnow;
33                         progress ((int) dltotal, last_dlnow);
34                 }
35                 return 0;
36         }
37
38         public void download (string url, string filename) throws IOError {
39                 print ("download (\"%s\", \"%s\")\n", url, filename);
40                 download_dir (Path.get_dirname (url) + "/");
41                 string basename = Path.get_basename (url);
42                 int size = file_size.lookup (basename);
43                 if (size > 0) {
44                         Posix.Stat st;
45                         Posix.stat (filename, out st);
46                         if (size == st.st_size) {
47                                 return;
48                         }
49                 }
50
51                 curl.setopt (Curl.Option.URL, url);
52                 curl.setopt (Curl.Option.WRITEFUNCTION, write_callback);
53                 curl.setopt (Curl.Option.WRITEDATA, this);
54                 curl.setopt (Curl.Option.NOPROGRESS, 0L);
55                 curl.setopt (Curl.Option.PROGRESSFUNCTION, progress_callback);
56                 curl.setopt (Curl.Option.PROGRESSDATA, this);
57
58                 last_dlnow = -1;
59                 file = FileStream.open (filename, "w");
60
61                 var res = curl.perform ();
62                 if (Curl.Code.ABORTED_BY_CALLBACK == res) {
63                                 throw new IOError.CANCELLED ("Download cancelled.");
64                 } else if (res != 0) {
65                         stderr.printf ("cURL performed: %d\n", res);
66                 }
67
68                 file = null;
69         }
70
71         void parse_dir_entry (string line) {
72                 try {
73                         Regex re_dir_entry = new Regex ("^.* ([0-9]*) [A-Z][a-z]* *[0-9]* [0-9]* [0-9]*:[0-9]* ([^ ]*)$");
74                         MatchInfo match_info;
75                         if (re_dir_entry.match (line, 0, out match_info)) {
76                                 string name = match_info.fetch (2);
77                                 int size = match_info.fetch (1).to_int ();
78                                 file_size.insert (name, size);
79                         }
80                 } catch (RegexError e) {
81                 }
82         }
83
84         string last_line = null;
85         [CCode (instance_pos = -1)]
86         size_t dir_callback (void *buffer, size_t size, size_t nmemb) {
87                 if (cancellable != null && cancellable.is_cancelled ())
88                         return 0;
89
90                 unowned char[] buf = (char[]) buffer;
91                 buf.length = (int) (size * nmemb);
92
93                 char *p = buf;
94                 int i;
95                 int j;
96                 for (i = 0, j = 0; i < buf.length; i++, j++) {
97                         if (buf[i] == '\n') {
98                                 buf[i] = 0;
99                                 if (last_line != null) {
100                                         parse_dir_entry (last_line + (string) p);
101                                         last_line = null;
102                                 } else {
103                                         parse_dir_entry ((string) p);
104                                 }
105                                 p += j + 1;
106                                 j = -1;
107                         }
108                 }
109                 if (j > 0)
110                         last_line = ((string) p).ndup (j);
111
112                 return buf.length;
113         }
114
115         public void download_dir (string url) throws IOError {
116                 if (dirname != null && dirname == url)
117                         return;
118                 print ("download_dir (\"%s\")\n", url);
119
120                 curl.setopt (Curl.Option.URL, url);
121                 curl.setopt (Curl.Option.WRITEFUNCTION, dir_callback);
122                 curl.setopt (Curl.Option.WRITEDATA, this);
123                 curl.setopt (Curl.Option.NOPROGRESS, 1L);
124                 curl.setopt (Curl.Option.PROGRESSFUNCTION, null);
125                 curl.setopt (Curl.Option.PROGRESSDATA, null);
126
127                 file_size = new HashTable<string, int> (str_hash, int_equal);
128
129                 var res = curl.perform ();
130                 if (Curl.Code.ABORTED_BY_CALLBACK == res) {
131                                 throw new IOError.CANCELLED ("Dir listing cancelled.");
132                 } else if (res != 0) {
133                         stderr.printf ("cURL performed: %d\n", res);
134                 }
135                 dirname = url;
136         }
137
138         public signal void progress (int dltotal, int dlnow);
139 }