394 lines
13 KiB
OCaml
394 lines
13 KiB
OCaml
open Config
|
|
open Gwdb
|
|
|
|
let portrait_folder conf = Util.base_path [ "images" ] conf.bname
|
|
|
|
let carrousel_folder conf =
|
|
Filename.concat (Util.base_path [ "src" ] conf.bname) "images"
|
|
|
|
(** [default_portrait_filename_of_key fn sn occ] is the default filename
|
|
of the corresponding person's portrait. WITHOUT its file extenssion.
|
|
e.g: default_portrait_filename_of_key "Jean Claude" "DUPOND" 3 is "jean_claude.3.dupond"
|
|
*)
|
|
let default_portrait_filename_of_key first_name surname occ =
|
|
let space_to_unders = Mutil.tr ' ' '_' in
|
|
let f = space_to_unders (Name.lower first_name) in
|
|
let s = space_to_unders (Name.lower surname) in
|
|
Format.sprintf "%s.%d.%s" f occ s
|
|
|
|
let default_portrait_filename base p =
|
|
default_portrait_filename_of_key (p_first_name base p) (p_surname base p)
|
|
(get_occ p)
|
|
|
|
let authorized_image_file_extension = [| ".jpg"; ".jpeg"; ".png"; ".gif" |]
|
|
|
|
let find_img_opt f =
|
|
let exists ext =
|
|
let fname = f ^ ext in
|
|
if Sys.file_exists fname then Some fname else None
|
|
in
|
|
match exists ".url" with
|
|
| Some f ->
|
|
let ic = open_in f in
|
|
let url = input_line ic in
|
|
close_in ic;
|
|
Some (`Url url)
|
|
| None -> (
|
|
match Mutil.array_find_map exists authorized_image_file_extension with
|
|
| None -> None
|
|
| Some f -> Some (`Path f))
|
|
|
|
(** [full_portrait_path conf base p] is [Some path] if [p] has a portrait.
|
|
[path] is a the full path of the file with file extension. *)
|
|
let full_portrait_path conf base p =
|
|
(* TODO why is extension not in filename..? *)
|
|
let s = default_portrait_filename base p in
|
|
let f = Filename.concat (portrait_folder conf) s in
|
|
match find_img_opt f with
|
|
| Some (`Path _) as full_path -> full_path
|
|
| Some (`Url _)
|
|
(* should not happen, there is only ".url" file in carrousel folder *)
|
|
| None ->
|
|
None
|
|
|
|
let source_filename conf src =
|
|
let fname1 = Filename.concat (carrousel_folder conf) src in
|
|
if Sys.file_exists fname1 then fname1
|
|
else
|
|
List.fold_right Filename.concat [ Secure.base_dir (); "src"; "images" ] src
|
|
|
|
let path_of_filename src =
|
|
let fname1 =
|
|
List.fold_right Filename.concat [ Secure.base_dir (); "images" ] src
|
|
in
|
|
if Sys.file_exists fname1 then `Path fname1
|
|
else `Path (Util.search_in_assets (Filename.concat "images" src))
|
|
|
|
let png_size ic =
|
|
let magic = really_input_string ic 4 in
|
|
if magic = "\137PNG" then (
|
|
seek_in ic 16;
|
|
let wid = input_binary_int ic in
|
|
let hei = input_binary_int ic in
|
|
Ok (wid, hei))
|
|
else Error ()
|
|
|
|
let gif_size ic =
|
|
let magic = really_input_string ic 4 in
|
|
if magic = "GIF8" then (
|
|
seek_in ic 6;
|
|
let wid =
|
|
let x = input_byte ic in
|
|
(input_byte ic * 256) + x
|
|
in
|
|
let hei =
|
|
let x = input_byte ic in
|
|
(input_byte ic * 256) + x
|
|
in
|
|
Ok (wid, hei))
|
|
else Error ()
|
|
|
|
let jpeg_size ic =
|
|
let magic = really_input_string ic 10 in
|
|
if
|
|
Char.code magic.[0] = 0xff
|
|
&& Char.code magic.[1] = 0xd8
|
|
&&
|
|
let m = String.sub magic 6 4 in
|
|
m = "JFIF" || m = "Exif"
|
|
then
|
|
let exif_type = String.sub magic 6 4 = "Exif" in
|
|
let rec loop found =
|
|
while Char.code (input_char ic) <> 0xFF do
|
|
()
|
|
done;
|
|
let ch =
|
|
let rec loop ch =
|
|
if Char.code ch = 0xFF then loop (input_char ic) else ch
|
|
in
|
|
loop (input_char ic)
|
|
in
|
|
if Char.code ch = 0xC0 || Char.code ch = 0xC3 then
|
|
if exif_type && not found then loop true
|
|
else (
|
|
for _i = 1 to 3 do
|
|
ignore @@ input_char ic
|
|
done;
|
|
let a = input_char ic in
|
|
let b = input_char ic in
|
|
let c = input_char ic in
|
|
let d = input_char ic in
|
|
let wid = (Char.code c lsl 8) lor Char.code d in
|
|
let hei = (Char.code a lsl 8) lor Char.code b in
|
|
Ok (wid, hei))
|
|
else
|
|
let a = input_char ic in
|
|
let b = input_char ic in
|
|
let len = (Char.code a lsl 8) lor Char.code b in
|
|
let len = if len >= 32768 then 0 else len in
|
|
for _i = 1 to len - 2 do
|
|
ignore @@ input_char ic
|
|
done;
|
|
if Char.code ch <> 0xDA then loop found else Error ()
|
|
in
|
|
loop false
|
|
else Error ()
|
|
|
|
let size_from_path fname =
|
|
(* TODO: size and mime type should be in db *)
|
|
let (`Path fname) = fname in
|
|
let res =
|
|
if fname = "" then Error ()
|
|
else
|
|
try
|
|
let ic = Secure.open_in_bin fname in
|
|
let r =
|
|
try
|
|
(* TODO: should match on mime type here *)
|
|
match String.lowercase_ascii @@ Filename.extension fname with
|
|
| ".jpeg" | ".jpg" -> jpeg_size ic
|
|
| ".png" -> png_size ic
|
|
| ".gif" -> gif_size ic
|
|
| _s -> Error ()
|
|
with End_of_file -> Error ()
|
|
in
|
|
close_in ic;
|
|
r
|
|
with Sys_error _e -> Error ()
|
|
in
|
|
res
|
|
|
|
let src_to_string = function `Url s | `Path s -> s
|
|
|
|
let scale_to_fit ~max_w ~max_h ~w ~h =
|
|
let w, h =
|
|
if h > max_h then
|
|
let w = w * max_h / h in
|
|
let h = max_h in
|
|
(w, h)
|
|
else (w, h)
|
|
in
|
|
let w, h =
|
|
if w > max_w then
|
|
let h = h * max_w / w in
|
|
let w = max_w in
|
|
(w, h)
|
|
else (w, h)
|
|
in
|
|
(w, h)
|
|
|
|
let is_not_private_img _conf fname =
|
|
not (Mutil.contains fname ("private" ^ Filename.dir_sep))
|
|
|
|
(** [has_access_to_portrait conf base p] is true iif we can see [p]'s portrait. *)
|
|
let has_access_to_portrait conf base p =
|
|
let img = get_image p in
|
|
(conf.wizard || conf.friend)
|
|
|| (not conf.no_image)
|
|
&& Util.authorized_age conf base p
|
|
&& ((not (is_empty_string img)) || full_portrait_path conf base p <> None)
|
|
&& is_not_private_img conf (sou base img)
|
|
(* TODO: privacy settings should be in db not in url *)
|
|
|
|
(** [has_access_to_carrousel conf base p] is true iif ???. *)
|
|
let has_access_to_carrousel conf base p =
|
|
(conf.wizard || conf.friend)
|
|
|| (not conf.no_image)
|
|
&& Util.authorized_age conf base p
|
|
&& not (Util.is_hide_names conf p)
|
|
|
|
let get_portrait_path conf base p =
|
|
if has_access_to_portrait conf base p then full_portrait_path conf base p
|
|
else None
|
|
|
|
(* parse a string to an `Url or a `Path *)
|
|
let urlorpath_of_string conf s =
|
|
let http = "http://" in
|
|
let https = "https://" in
|
|
if Mutil.start_with http 0 s || Mutil.start_with https 0 s then `Url s
|
|
else if Filename.is_implicit s then
|
|
match List.assoc_opt "images_path" conf.base_env with
|
|
| Some p when p <> "" -> `Path (Filename.concat p s)
|
|
| Some _ | None ->
|
|
let fname = Filename.concat (portrait_folder conf) s in
|
|
`Path fname
|
|
else `Path s
|
|
|
|
let src_of_string conf s =
|
|
if s = "" then `Empty
|
|
else
|
|
let l = String.length s - 1 in
|
|
if s.[l] = ')' then `Src_with_size_info s else urlorpath_of_string conf s
|
|
|
|
let parse_src_with_size_info conf s =
|
|
let (`Src_with_size_info s) = s in
|
|
let l = String.length s - 1 in
|
|
try
|
|
let pos1 = String.index s '(' in
|
|
let pos2 = String.index_from s pos1 'x' in
|
|
let w = String.sub s (pos1 + 1) (pos2 - pos1 - 1) |> int_of_string in
|
|
let h = String.sub s (pos2 + 1) (l - pos2 - 1) |> int_of_string in
|
|
let s = String.sub s 0 pos1 in
|
|
Ok (urlorpath_of_string conf s, (w, h))
|
|
with Not_found | Failure _ ->
|
|
!GWPARAM.syslog `LOG_ERR
|
|
(Format.sprintf "Error parsing portrait source with size info %s" s);
|
|
Error "Failed to parse url with size info"
|
|
|
|
let get_portrait conf base p =
|
|
if has_access_to_portrait conf base p then
|
|
match src_of_string conf (sou base (get_image p)) with
|
|
| `Src_with_size_info _s as s_info -> (
|
|
match parse_src_with_size_info conf s_info with
|
|
| Error _e -> None
|
|
| Ok (s, _size) -> Some s)
|
|
| `Url _s as url -> Some url
|
|
| `Path p as path -> if Sys.file_exists p then Some path else None
|
|
| `Empty -> full_portrait_path conf base p
|
|
else None
|
|
|
|
(* In images/carrousel we store either
|
|
- the image as the original image.jpg/png/tif image
|
|
- the url to the image as content of a image.url text file
|
|
*)
|
|
let get_old_portrait conf base p =
|
|
if has_access_to_portrait conf base p then
|
|
let key = default_portrait_filename base p in
|
|
let f =
|
|
Filename.concat (Filename.concat (portrait_folder conf) "old") key
|
|
in
|
|
find_img_opt f
|
|
else None
|
|
|
|
let rename_portrait conf base p (nfn, nsn, noc) =
|
|
match get_portrait conf base p with
|
|
| Some (`Path old_f) -> (
|
|
let new_s = default_portrait_filename_of_key nfn nsn noc in
|
|
let old_s = default_portrait_filename base p in
|
|
let f = Filename.concat (portrait_folder conf) new_s in
|
|
let old_ext = Filename.extension old_f in
|
|
let new_f = f ^ old_ext in
|
|
(try Sys.rename old_f new_f
|
|
with Sys_error e ->
|
|
!GWPARAM.syslog `LOG_ERR
|
|
(Format.sprintf
|
|
"Error renaming portrait: old_path=%s new_path=%s : %s" old_f
|
|
new_f e));
|
|
let new_s_f =
|
|
String.concat Filename.dir_sep [ portrait_folder conf; "old"; new_s ]
|
|
in
|
|
let old_s_f =
|
|
String.concat Filename.dir_sep [ portrait_folder conf; "old"; old_s ]
|
|
in
|
|
(if Sys.file_exists (old_s_f ^ old_ext) then
|
|
try Sys.rename (old_s_f ^ old_ext) (new_s_f ^ old_ext)
|
|
with Sys_error e ->
|
|
!GWPARAM.syslog `LOG_ERR
|
|
(Format.sprintf
|
|
"Error renaming old portrait: old_path=%s new_path=%s : %s" old_f
|
|
new_f e));
|
|
let new_s_f =
|
|
String.concat Filename.dir_sep [ carrousel_folder conf; new_s ]
|
|
in
|
|
let old_s_f =
|
|
String.concat Filename.dir_sep [ carrousel_folder conf; old_s ]
|
|
in
|
|
if Sys.file_exists old_s_f then
|
|
try Sys.rename old_s_f new_s_f
|
|
with Sys_error e ->
|
|
!GWPARAM.syslog `LOG_ERR
|
|
(Format.sprintf
|
|
"Error renaming carrousel store: old_path=%s new_path=%s : %s"
|
|
old_f new_f e))
|
|
| Some (`Url _url) -> () (* old url still applies *)
|
|
| None -> ()
|
|
|
|
let get_portrait_with_size conf base p =
|
|
if has_access_to_portrait conf base p then
|
|
match src_of_string conf (sou base (get_image p)) with
|
|
| `Src_with_size_info _s as s_info -> (
|
|
match parse_src_with_size_info conf s_info with
|
|
| Error _e -> None
|
|
| Ok (s, size) -> Some (s, Some size))
|
|
| `Url _s as url -> Some (url, None)
|
|
| `Path p as path ->
|
|
if Sys.file_exists p then
|
|
Some (path, size_from_path path |> Result.to_option)
|
|
else None
|
|
| `Empty -> (
|
|
match full_portrait_path conf base p with
|
|
| None -> None
|
|
| Some path -> Some (path, size_from_path path |> Result.to_option))
|
|
else None
|
|
|
|
(* For carrousel ************************************ *)
|
|
|
|
let carrousel_file_path conf base p fname old =
|
|
let dir =
|
|
let dir = default_portrait_filename base p in
|
|
if old then Filename.concat dir "old" else dir
|
|
in
|
|
String.concat Filename.dir_sep
|
|
([ carrousel_folder conf; dir ] @ if fname = "" then [] else [ fname ])
|
|
|
|
let get_carrousel_file_content conf base p fname kind old =
|
|
let fname =
|
|
Filename.chop_extension (carrousel_file_path conf base p fname old) ^ kind
|
|
in
|
|
if Sys.file_exists fname then (
|
|
let ic = Secure.open_in fname in
|
|
let s = really_input_string ic (in_channel_length ic) in
|
|
close_in ic;
|
|
if s = "" then None else Some s)
|
|
else None
|
|
|
|
(* get list of files in carrousel *)
|
|
let get_carrousel_img_aux conf base p old =
|
|
let get_carrousel_img_note fname =
|
|
Option.value ~default:""
|
|
(get_carrousel_file_content conf base p fname ".txt" false)
|
|
in
|
|
let get_carrousel_img_src fname =
|
|
Option.value ~default:""
|
|
(get_carrousel_file_content conf base p fname ".src" false)
|
|
in
|
|
let get_carrousel_img fname =
|
|
let path = carrousel_file_path conf base p fname old in
|
|
find_img_opt (Filename.chop_extension path)
|
|
in
|
|
if not (has_access_to_carrousel conf base p) then []
|
|
else
|
|
let f = carrousel_file_path conf base p "" old in
|
|
try
|
|
if Sys.file_exists f && Sys.is_directory f then
|
|
Array.fold_left
|
|
(fun acc f1 ->
|
|
let ext = Filename.extension f1 in
|
|
if
|
|
f1 <> ""
|
|
&& f1.[0] <> '.'
|
|
&& (Array.mem ext authorized_image_file_extension || ext = ".url")
|
|
then
|
|
match get_carrousel_img f1 with
|
|
| None -> acc
|
|
| Some (`Path path) ->
|
|
(path, "", get_carrousel_img_src f1, get_carrousel_img_note f1)
|
|
:: acc
|
|
| Some (`Url url) ->
|
|
( Filename.chop_extension (Filename.basename f1) ^ ".url",
|
|
url,
|
|
get_carrousel_img_src f1,
|
|
get_carrousel_img_note f1 )
|
|
:: acc
|
|
else acc)
|
|
[] (Sys.readdir f)
|
|
else []
|
|
with Sys_error e ->
|
|
!GWPARAM.syslog `LOG_ERR (Format.sprintf "carrousel error: %s, %s" f e);
|
|
[]
|
|
|
|
let get_carrousel_imgs conf base p = get_carrousel_img_aux conf base p false
|
|
let get_carrousel_old_imgs conf base p = get_carrousel_img_aux conf base p true
|
|
|
|
(* end carrousel ************************************ *)
|