source: code/trunk/cli/pull.ml@ 59

Last change on this file since 59 was 42, checked in by fox, 2 years ago

Preliminary support for cross-domain references

File size: 7.1 KB
Line 
1let writer accum data =
2 Buffer.add_string accum data;
3 String.length data
4
5let showContent content =
6 Printf.printf "%s" (Buffer.contents content);
7 flush stdout
8
9let showInfo connection =
10 Printf.printf "Time: %f for: %s\n"
11 (Curl.get_totaltime connection)
12 (Curl.get_effectiveurl connection)
13
14let getContent connection url =
15 Curl.set_url connection url;
16 Curl.perform connection
17
18let curl_pull url =
19 let result = Buffer.create 4069
20 and errorBuffer = ref "" in
21 let connection = Curl.init () in
22 try
23 Curl.set_errorbuffer connection errorBuffer;
24 Curl.set_writefunction connection (writer result);
25 Curl.set_followlocation connection true;
26 Curl.set_url connection url;
27 Curl.perform connection;
28(* showContent result;*)
29(* showInfo connection;*)
30 Curl.cleanup connection;
31 Ok result
32 with
33 | Curl.CurlException (_reason, _code, _str) ->
34 Curl.cleanup connection;
35 Error (Printf.sprintf "Error: %s %s" url !errorBuffer)
36 | Failure s ->
37 Curl.cleanup connection;
38 Error (Printf.sprintf "Caught exception: %s" s)
39
40let newer time id dir =
41 match Logarion.File_store.to_text @@ Filename.(concat dir (Logarion.Id.short id) ^ ".txt") with
42 | Error x -> prerr_endline x; true
43 | Ok txt -> time > (Logarion.(Header_pack.date (Date.listing txt.date)))
44 | exception (Sys_error _) -> true
45
46let print_peers p =
47 let open Logarion.Header_pack in
48 match Msgpck.to_list p.peers with [] -> ()
49 | ps -> print_endline @@
50 List.fold_left (fun a x -> Printf.sprintf "%s %s" a (Msgpck.to_string x)) "peers: " ps
51
52type filter_t = { authors: Logarion.Person.Set.t; topics: Logarion.String_set.t }
53
54let print_pull_start width total title dir =
55 Printf.printf "%*d/%s %s => %s %!" width 0 total title dir
56
57let print_pull width total i =
58 Printf.printf "\r%*d/%s %!" width (i+1) total
59
60let printers total title dir =
61 let width = String.length total in
62 print_pull_start width total title dir;
63 print_pull width total
64
65let fname dir text = Filename.concat dir (Logarion.Text.short_id text ^ ".txt")
66
67let pull_text url dir id =
68 let u = Filename.concat url ((Logarion.Id.short id) ^ ".txt") in
69 match curl_pull u with
70 | Error msg -> Printf.eprintf "Failed getting %s: %s" u msg
71 | Ok txt -> let txt = Buffer.contents txt in
72 match Logarion.Text.of_string txt with
73 | Error s -> prerr_endline s
74 | Ok text ->
75 let file = open_out_gen [Open_creat; Open_trunc; Open_wronly] 0o640 (fname dir text) in
76 output_string file txt; close_out file
77
78let per_text url dir filter print i id time title authors topics _refs _reps = match id with
79 | "" -> Printf.eprintf "\nInvalid id for %s\n" title
80 | id -> let open Logarion in
81 print i;
82 if newer time id dir
83 && (String_set.empty = filter.topics
84 || String_set.exists (fun t -> List.mem t topics) filter.topics)
85 && (Person.Set.empty = filter.authors
86 || Person.Set.exists (fun t -> List.mem (Person.to_string t) authors) filter.authors)
87 then pull_text url dir id
88
89(*TODO: integrate in lib*)
90let validate_id_length s = String.length s <= 32
91let validate_id_chars s = try
92 String.iter (function 'a'..'z'|'A'..'Z'|'0'..'9'-> () | _ -> raise (Invalid_argument "")) s;
93 true
94 with Invalid_argument _ -> false
95
96let pull_index url authors_opt topics_opt =
97 let index_url = Filename.concat url "index.pck" in
98 match curl_pull index_url with
99 | Error s -> prerr_endline s; false
100 | Ok body ->
101 match Logarion.Header_pack.of_string (Buffer.contents body) with
102 | Error s -> Printf.printf "Error with %s: %s\n" url s; false
103 | Ok pk when pk.info.id = "" ->
104 Printf.printf "Empty ID index.pck, skipping %s\n" url; false
105 | Ok pk when not (validate_id_length pk.info.id) ->
106 Printf.printf "Index pack ID longer than 32 characters, skipping %s\n" url; false
107 | Ok pk when not (validate_id_chars pk.info.id) ->
108 Printf.printf "Index pack contains invalid ID characters, skipping %s\n" url; false
109 | Ok pk ->
110 let dir = Filename.concat Logarion.Peers.text_dir pk.info.id in
111 Logarion.File_store.with_dir dir;
112 let file = open_out_gen [Open_creat; Open_trunc; Open_wronly] 0o640
113 (Filename.concat dir "index.pck") in
114 output_string file ( Logarion.Header_pack.string {
115 pk with info = { pk.info with locations = url::pk.info.locations }});
116 close_out file;
117 let filter = let open Logarion in {
118 authors = (match authors_opt with Some s -> Person.Set.of_string s | None -> Person.Set.empty);
119 topics =( match topics_opt with Some s -> String_set.of_string s | None -> String_set.empty);
120 } in
121 let name = match pk.info.title with "" -> url | title -> title in
122 let print = printers (string_of_int @@ Logarion.Header_pack.numof_texts pk) name dir in
123 try Logarion.Header_pack.iteri (per_text url dir filter print) pk; print_newline (); true
124 with Invalid_argument msg -> Printf.printf "\nFailed to parse %s: %s\n%!" url msg; false
125
126let pull_list auths topics =
127 Curl.global_init Curl.CURLINIT_GLOBALALL;
128 let pull got_one peer_url = if got_one then got_one else
129 (pull_index peer_url auths topics) in
130 let open Logarion in
131 let fold_locations init peer =
132 ignore @@ List.fold_left pull init peer.Peers.pack.Header_pack.info.locations;
133 false
134 in
135 ignore @@ Peers.fold fold_locations false;
136 Curl.global_cleanup ()
137
138let pull url auths topics = match url with
139 | "" -> pull_list auths topics | x -> ignore (pull_index x auths topics)
140
141open Cmdliner
142let term =
143 let authors = Arg.(value & opt (some string) None & info ["a"; "authors"]
144 ~docv:"comma-separated names" ~doc:"filter by authors") in
145 let topics = Arg.(value & opt (some string) None & info ["t"; "topics"]
146 ~docv:"comma-separated topics" ~doc:"filter by topics") in
147 let url = Arg.(value & pos 0 string "" & info [] ~docv:"URL"
148 ~doc:"Repository location") in
149 Term.(const pull $ url $ authors $ topics),
150 Term.info "pull" ~doc:"pull listed texts" ~man:[ `S "DESCRIPTION";
151 `P "Pull texts from known repositories. To add a new repository use:";
152 `P "txt pull [url]";
153 `P ("This creates a directory in " ^ Logarion.Peers.text_dir
154 ^ " and downloads the text index.pck file in it")]
155
156(*module Msg = struct*)
157(* type t = string * string*)
158(* let compare (x0,y0) (x1,y1) =*)
159(* match compare x1 x0 with 0 -> String.compare y0 y1 | c -> c*)
160(*end*)
161(*module MsgSet = Set.Make(Msg)*)
162(*let pull_msgs url _authors _topics =*)
163(* match http_apply response url with*)
164(* | Error msg ->*)
165(* Printf.eprintf "Failed index request for %s %s" url msg*)
166(* | Ok body ->*)
167(* let rec fold_msgs s a fn =*)
168(* let t, msg = Scanf.bscanf s "%s %s@\n" (fun t m -> t, m) in*)
169(* if t <> "" then fold_msgs s (fn a t msg) fn else a*)
170(* in*)
171(* let s = Scanf.Scanning.from_string body in*)
172(* let msgs = MsgSet.empty in*)
173(* let date_string t = Ptime.to_date t |>*)
174(* fun (y, m, d) -> Printf.sprintf "%04d-%02d-%02d" y m d in*)
175(* let msgs = fold_msgs s msgs*)
176(* (fun msgs t m -> match Ptime.of_rfc3339 t with*)
177(* | Ok (v,_,_) -> let open MsgSet in*)
178(* let msgs = if cardinal msgs > 1 then remove (max_elt msgs) msgs else msgs in*)
179(* add (v,m) msgs*)
180(* | _ -> msgs) in*)
181(* let msg_string = MsgSet.fold*)
182(* (fun (t,m) a -> a ^ Printf.sprintf " %s 𐄁 %s\n" (date_string t) m)*)
183(* msgs "" in*)
184(* Printf.printf "┌───{ %s }───┐\n%s" url msg_string*)
Note: See TracBrowser for help on using the repository browser.