Source file github_cookie_jar.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
open Printf
open Lwt
type t = { jar_path : string }
exception InvalidName of string
let invalid_names = Re.(List.map compile [
seq [bos; str "."];
str "../";
seq [bos; str Filename.dir_sep];
seq [str Filename.dir_sep; eos];
])
let jar_path { jar_path } = jar_path
let file_kind_match path ~reg ~dir ~other = Lwt_unix.(
stat path
>>= fun { st_kind; _ } -> match st_kind with
| S_REG -> reg ()
| S_DIR -> dir ()
| S_CHR | S_BLK | S_LNK | S_FIFO | S_SOCK -> other ()
)
let rec mkdir_p dir =
match Sys.file_exists dir with
| true -> return ()
| false ->
mkdir_p (Filename.dirname dir)
>>= fun () -> Lwt_unix.mkdir dir 0o700
let rec init ?jar_path () =
let jar_path = match jar_path with
| None ->
let home = try Sys.getenv "HOME" with Not_found -> "." in
let basedir = Filename.concat home ".github" in
Filename.concat basedir "jar"
| Some jar_path -> jar_path
in
match Sys.file_exists jar_path with
| true -> return { jar_path }
| false ->
printf "Github cookie jar: initialized %s\n" jar_path;
mkdir_p jar_path
>>= init ~jar_path
let save ({ jar_path } as jar) ~name ~auth =
(if List.exists (fun re -> Re.execp re name) invalid_names then
fail (InvalidName name)
else
return ()
) >>= fun () ->
let rec backup_path ?(dirok=false) name =
let fullname = Filename.concat jar_path name in
let backup () =
let open Unix in
let tm = gmtime (gettimeofday ()) in
let backfname = sprintf "%s.%.4d%.2d%.2d.%2d%2d%2d.bak"
name (1900 + tm.tm_year) (1 + tm.tm_mon) tm.tm_mday
tm.tm_hour tm.tm_min tm.tm_sec in
let fullback = Filename.concat jar_path backfname in
printf "Github cookie jar: backing up\n%s -> %s\n" fullname fullback;
Lwt_unix.rename fullname fullback
in
catch (fun () ->
file_kind_match fullname
~reg:backup
~dir:(if dirok then return else backup)
~other:backup
) (function
| Unix.Unix_error (Unix.ENOENT, _, _)
| Unix.Unix_error (Unix.ENOTDIR, _, _) ->
begin match Filename.dirname name with
| "." -> return ()
| parent -> backup_path ~dirok:true parent
end
| exn -> fail exn
)
in
backup_path name
>>= fun () ->
let fullname = Filename.concat jar_path name in
mkdir_p (Filename.dirname fullname)
>>= fun () ->
let auth_fd = Unix.(openfile fullname [O_CREAT; O_TRUNC; O_WRONLY] 0o600) in
let auth_oc = Unix.out_channel_of_descr auth_fd in
fprintf auth_oc "%s" (Github_j.string_of_auth auth);
close_out auth_oc;
printf "Github cookie jar: created %s\n" fullname;
return jar
let delete jar ~name =
if List.exists (fun re -> Re.execp re name) invalid_names then
fail (InvalidName name)
else
Lwt_unix.unlink (Filename.concat jar.jar_path name)
>>= fun () ->
return jar
let read_auth_file { jar_path } name =
let fname = Filename.concat jar_path name in
let { Unix.st_perm; _ } = Unix.stat fname in
let safe_perm = 0o7770 land st_perm in
begin if safe_perm <> st_perm
then Unix.chmod fname safe_perm
end;
Lwt_io.with_file ~mode:Lwt_io.input fname
(fun ic ->
Lwt_stream.fold_s (fun b a -> return (a^b)) (Lwt_io.read_lines ic) ""
>>= fun buf ->
return (Github_j.auth_of_string buf)
)
let get_all ({ jar_path } as jar) =
let rec traverse dir =
let base = Filename.concat jar_path dir in
let files = Lwt_unix.files_of_directory base in
Lwt_stream.fold_s (fun b a ->
if b = "." || b = ".." then return a else begin
let path = Filename.concat base b in
let ident = Filename.concat dir b in
file_kind_match path
~reg:(fun () ->
read_auth_file jar ident
>>= fun auth ->
return ((ident,auth)::a))
~dir:(fun () ->
traverse ident
>>= fun sub ->
return (sub@a))
~other:(fun () -> return a)
end
) files []
in traverse ""
let get jar ~name =
catch (fun () ->
read_auth_file jar name
>>= fun auth ->
return (Some auth)
) (fun _ -> return_none)