···
* https://datatracker.ietf.org/doc/html/rfc8620
6
+
(** Whether to redact sensitive information *)
7
+
let should_redact_sensitive = ref true
9
+
(** Initialize and configure logging for JMAP *)
10
+
let init_logging ?(level=2) ?(enable_logs=true) ?(redact_sensitive=true) () =
11
+
if enable_logs then begin
12
+
Logs.set_reporter (Logs.format_reporter ());
14
+
| 0 -> Logs.set_level None
15
+
| 1 -> Logs.set_level (Some Logs.Error)
16
+
| 2 -> Logs.set_level (Some Logs.Info)
17
+
| 3 -> Logs.set_level (Some Logs.Debug)
18
+
| _ -> Logs.set_level (Some Logs.Debug)
20
+
Logs.set_level None;
21
+
should_redact_sensitive := redact_sensitive
23
+
(** Redact sensitive data like tokens *)
24
+
let redact_token ?(redact=true) token =
25
+
if redact && !should_redact_sensitive && String.length token > 8 then
26
+
let prefix = String.sub token 0 4 in
27
+
let suffix = String.sub token (String.length token - 4) 4 in
28
+
prefix ^ "..." ^ suffix
32
+
(** Redact sensitive headers like Authorization *)
33
+
let redact_headers headers =
34
+
List.map (fun (k, v) ->
35
+
if String.lowercase_ascii k = "authorization" then
36
+
if !should_redact_sensitive then
37
+
let parts = String.split_on_char ' ' v in
39
+
| scheme :: token :: _ -> (k, scheme ^ " " ^ redact_token token)
45
+
(* Initialize logging with defaults *)
46
+
let () = init_logging ()
(** Module for managing JMAP capability URIs and other constants *)
module Capability = struct
(** Core JMAP capability URI *)
···
let open Cohttp_lwt_unix in
let headers = Header.add_list (Header.init ()) headers in
451
-
(* Debug: print request details *)
452
-
Printf.printf "\n===== HTTP REQUEST =====\n";
453
-
Printf.printf "URI: %s\n" (Uri.to_string uri);
454
-
Printf.printf "METHOD: %s\n" method_;
455
-
Printf.printf "HEADERS:\n";
456
-
Header.iter (fun k v -> Printf.printf " %s: %s\n" k v) headers;
457
-
Printf.printf "BODY:\n%s\n" body;
458
-
Printf.printf "======================\n\n";
493
+
(* Log request details at debug level *)
494
+
let header_list = Cohttp.Header.to_list headers in
495
+
let redacted_headers = redact_headers header_list in
496
+
Logs.debug (fun m ->
497
+
m "\n===== HTTP REQUEST =====\n\
502
+
======================\n"
503
+
(Uri.to_string uri)
505
+
(String.concat "\n" (List.map (fun (k, v) -> Printf.sprintf " %s: %s" k v) redacted_headers))
···
let* body_str = Cohttp_lwt.Body.to_string body in
let status = Response.status resp |> Code.code_of_status in
471
-
(* Debug: print response details *)
472
-
Printf.printf "\n===== HTTP RESPONSE =====\n";
473
-
Printf.printf "STATUS: %d\n" status;
474
-
Printf.printf "HEADERS:\n";
475
-
Response.headers resp |> Header.iter (fun k v -> Printf.printf " %s: %s\n" k v);
476
-
Printf.printf "BODY:\n%s\n" body_str;
477
-
Printf.printf "========================\n\n";
519
+
(* Log response details at debug level *)
520
+
let header_list = Cohttp.Header.to_list (Response.headers resp) in
521
+
let redacted_headers = redact_headers header_list in
522
+
Logs.debug (fun m ->
523
+
m "\n===== HTTP RESPONSE =====\n\
527
+
======================\n"
529
+
(String.concat "\n" (List.map (fun (k, v) -> Printf.sprintf " %s: %s" k v) redacted_headers))
if status >= 200 && status < 300 then
···
Lwt.return (Error (HTTP_error (status, body_str))))
let error_msg = Printexc.to_string e in
485
-
Printf.printf "\n===== HTTP ERROR =====\n%s\n======================\n\n" error_msg;
538
+
Logs.err (fun m -> m "%s" error_msg);
Lwt.return (Error (Connection_error error_msg)))
(** Make a raw JMAP API request
···
(* API token (bearer authentication) *)
"Bearer " ^ config.authentication_token
502
-
Printf.printf "Using authorization header: %s\n" auth_header;
556
+
(* Log auth header at debug level with redaction *)
557
+
let redacted_header =
558
+
if String.length config.username > 0 then
559
+
"Basic " ^ redact_token (Base64.encode_string (config.username ^ ":" ^ config.authentication_token))
561
+
"Bearer " ^ redact_token config.authentication_token
563
+
Logs.debug (fun m -> m "Using authorization header: %s" redacted_header);
("Content-Type", "application/json");
("Content-Length", string_of_int (String.length body));
···
(match parse_json_string response_body with
512
-
| Ok json -> Lwt.return (parse_response json)
513
-
| Error e -> Lwt.return (Error e))
514
-
| Error e -> Lwt.return (Error e)
575
+
Logs.debug (fun m -> m "Successfully parsed JSON response");
576
+
Lwt.return (parse_response json)
578
+
let msg = match e with Parse_error m -> m | _ -> "unknown error" in
579
+
Logs.err (fun m -> m "Failed to parse response: %s" msg);
580
+
Lwt.return (Error e))
583
+
| Connection_error msg -> Logs.err (fun m -> m "Connection error: %s" msg)
584
+
| HTTP_error (code, _) -> Logs.err (fun m -> m "HTTP error %d" code)
585
+
| Parse_error msg -> Logs.err (fun m -> m "Parse error: %s" msg)
586
+
| Authentication_error -> Logs.err (fun m -> m "Authentication error"));
587
+
Lwt.return (Error e)
(** Parse a JSON object as a Session object *)
let parse_session_object json =
···
match (username, authentication_token, api_token) with
let auth = "Basic " ^ Base64.encode_string (u ^ ":" ^ t) in
580
-
Printf.printf "Session using Basic auth: %s\n" auth;
653
+
let redacted_auth = "Basic " ^ redact_token (Base64.encode_string (u ^ ":" ^ t)) in
654
+
Logs.info (fun m -> m "Session using Basic auth: %s" redacted_auth);
("Content-Type", "application/json");
let auth = "Bearer " ^ token in
587
-
Printf.printf "Session using Bearer auth: %s\n" auth;
661
+
let redacted_token = redact_token token in
662
+
Logs.info (fun m -> m "Session using Bearer auth: %s" ("Bearer " ^ redacted_token));
("Content-Type", "application/json");
···
(match parse_json_string response_body with
599
-
| Ok json -> Lwt.return (parse_session_object json)
600
-
| Error e -> Lwt.return (Error e))
601
-
| Error e -> Lwt.return (Error e)
675
+
Logs.debug (fun m -> m "Successfully parsed session response");
676
+
Lwt.return (parse_session_object json)
678
+
let msg = match e with Parse_error m -> m | _ -> "unknown error" in
679
+
Logs.err (fun m -> m "Failed to parse session response: %s" msg);
680
+
Lwt.return (Error e))
682
+
let err_msg = match e with
683
+
| Connection_error msg -> "Connection error: " ^ msg
684
+
| HTTP_error (code, _) -> Printf.sprintf "HTTP error %d" code
685
+
| Parse_error msg -> "Parse error: " ^ msg
686
+
| Authentication_error -> "Authentication error"
688
+
Logs.err (fun m -> m "Failed to get session: %s" err_msg);
689
+
Lwt.return (Error e)
(** Upload a binary blob to the server