class IMDbFtpDownloader { Curl.EasyHandle curl; private ZLib.InflateStream strm; private int percent; private char[] buf_out; private uint have; private LineParser parser; private Cancellable cancellable; [CCode (instance_pos = -1)] size_t write_callback (void *buffer, size_t size, size_t nmemb) { if (cancellable != null && cancellable.is_cancelled ()) return 0; strm.next_in = buffer; strm.avail_in = (uint) (size * nmemb); if (strm.avail_in == 0) return 0; do { strm.next_out = (char*) buf_out + have; strm.avail_out = buf_out.length - have; char* p = (char*) buf_out; var ret = strm.inflate (ZLib.Flush.NO_FLUSH); assert (ret != ZLib.Status.STREAM_ERROR); if (ret == ZLib.Status.NEED_DICT) ret = ZLib.Status.DATA_ERROR; switch (ret) { case ZLib.Status.DATA_ERROR: case ZLib.Status.MEM_ERROR: return ret; } have = buf_out.length - strm.avail_out; char* l = p; int j = 0; for (int i = 0; i < have; i++, j++) { if (p[i] == '\n') { p[i] = 0; if (parser != null) parser.parse_line ((string) l); j = -1; l = p + i + 1; } } if (j > 0) { Memory.copy (p, l, j); have = j; } else { have = 0; } } while (strm.avail_out == 0); return size * nmemb; } int progress_callback (double dltotal, double dlnow, double ultotal, double ulnow) { if (cancellable != null && cancellable.is_cancelled ()) return 1; if (dltotal > 0) { int p = (int) (100 * dlnow / dltotal); if (p > percent) { percent = p; progress_changed (p); } } return 0; } public IMDbFtpDownloader (Cancellable? _cancellable) { cancellable = _cancellable; curl = new Curl.EasyHandle (); curl.setopt (Curl.Option.WRITEFUNCTION, write_callback); curl.setopt (Curl.Option.WRITEDATA, this); curl.setopt (Curl.Option.NOPROGRESS, 0L); curl.setopt (Curl.Option.PROGRESSFUNCTION, progress_callback); curl.setopt (Curl.Option.PROGRESSDATA, this); buf_out = new char[16384]; } public void download (string url, LineParser? _parser) throws IOError { curl.setopt (Curl.Option.URL, url); percent = 0; parser = _parser; have = 0; strm = ZLib.InflateStream.full (15 | 32); var res = curl.perform (); if (Curl.Code.ABORTED_BY_CALLBACK == res) { throw new IOError.CANCELLED ("Download cancelled."); } } public signal void progress_changed (int percent); }