this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at if-only 406 lines 13 kB view raw
1(* 2 * jmap_vacation_manager.ml - Manage vacation/out-of-office auto-responses 3 * 4 * This binary demonstrates JMAP's vacation response capabilities for setting 5 * up and managing automatic email responses. 6 * 7 * For step 2, we're only testing type checking. No implementations required. 8 *) 9 10open Cmdliner 11 12(** Vacation response actions **) 13type vacation_action = 14 | Show 15 | Enable of vacation_config 16 | Disable 17 | Update of vacation_config 18 19and vacation_config = { 20 subject : string option; 21 text_body : string; 22 html_body : string option; 23 from_date : float option; 24 to_date : float option; 25 exclude_addresses : string list; 26} 27 28(** Command-line arguments **) 29 30let host_arg = 31 Arg.(required & opt (some string) None & info ["h"; "host"] 32 ~docv:"HOST" ~doc:"JMAP server hostname") 33 34let user_arg = 35 Arg.(required & opt (some string) None & info ["u"; "user"] 36 ~docv:"USERNAME" ~doc:"Username for authentication") 37 38let password_arg = 39 Arg.(required & opt (some string) None & info ["p"; "password"] 40 ~docv:"PASSWORD" ~doc:"Password for authentication") 41 42let enable_arg = 43 Arg.(value & flag & info ["e"; "enable"] 44 ~doc:"Enable vacation response") 45 46let disable_arg = 47 Arg.(value & flag & info ["d"; "disable"] 48 ~doc:"Disable vacation response") 49 50let show_arg = 51 Arg.(value & flag & info ["s"; "show"] 52 ~doc:"Show current vacation settings") 53 54let subject_arg = 55 Arg.(value & opt (some string) None & info ["subject"] 56 ~docv:"SUBJECT" ~doc:"Vacation email subject line") 57 58let message_arg = 59 Arg.(value & opt (some string) None & info ["m"; "message"] 60 ~docv:"TEXT" ~doc:"Vacation message text") 61 62let message_file_arg = 63 Arg.(value & opt (some string) None & info ["message-file"] 64 ~docv:"FILE" ~doc:"Read vacation message from file") 65 66let html_message_arg = 67 Arg.(value & opt (some string) None & info ["html-message"] 68 ~docv:"HTML" ~doc:"HTML vacation message") 69 70let from_date_arg = 71 Arg.(value & opt (some string) None & info ["from-date"] 72 ~docv:"DATE" ~doc:"Start date for vacation (YYYY-MM-DD)") 73 74let to_date_arg = 75 Arg.(value & opt (some string) None & info ["to-date"] 76 ~docv:"DATE" ~doc:"End date for vacation (YYYY-MM-DD)") 77 78let exclude_arg = 79 Arg.(value & opt_all string [] & info ["exclude"] 80 ~docv:"EMAIL" ~doc:"Email address to exclude from auto-response") 81 82(** Helper functions **) 83 84(* Parse date string to Unix timestamp *) 85let parse_date date_str = 86 try 87 let (year, month, day) = Scanf.sscanf date_str "%d-%d-%d" (fun y m d -> (y, m, d)) in 88 let tm = Unix.{ tm_sec = 0; tm_min = 0; tm_hour = 0; 89 tm_mday = day; tm_mon = month - 1; tm_year = year - 1900; 90 tm_wday = 0; tm_yday = 0; tm_isdst = false } in 91 Some (Unix.mktime tm |> fst) 92 with _ -> 93 Printf.eprintf "Invalid date format: %s (use YYYY-MM-DD)\n" date_str; 94 None 95 96(* Format Unix timestamp as date string *) 97let format_date timestamp = 98 let tm = Unix.localtime timestamp in 99 Printf.sprintf "%04d-%02d-%02d" 100 (tm.Unix.tm_year + 1900) (tm.Unix.tm_mon + 1) tm.Unix.tm_mday 101 102(* Read file contents *) 103let read_file filename = 104 let ic = open_in filename in 105 let len = in_channel_length ic in 106 let content = really_input_string ic len in 107 close_in ic; 108 content 109 110(* Display vacation response settings *) 111let show_vacation_response vacation = 112 Printf.printf "\nVacation Response Settings:\n"; 113 Printf.printf "==========================\n\n"; 114 115 Printf.printf "Status: %s\n" 116 (if Jmap_email.Vacation.Vacation_response.is_enabled vacation then "ENABLED" else "DISABLED"); 117 118 (match Jmap_email.Vacation.Vacation_response.subject vacation with 119 | Some subj -> Printf.printf "Subject: %s\n" subj 120 | None -> Printf.printf "Subject: (default)\n"); 121 122 (match Jmap_email.Vacation.Vacation_response.text_body vacation with 123 | Some body -> 124 Printf.printf "\nMessage:\n"; 125 Printf.printf "--------\n"; 126 Printf.printf "%s\n" body; 127 Printf.printf "--------\n" 128 | None -> Printf.printf "\nMessage: (none set)\n"); 129 130 (match Jmap_email.Vacation.Vacation_response.from_date vacation with 131 | Some date -> Printf.printf "\nActive from: %s\n" (format_date date) 132 | None -> ()); 133 134 (match Jmap_email.Vacation.Vacation_response.to_date vacation with 135 | Some date -> Printf.printf "Active until: %s\n" (format_date date) 136 | None -> ()); 137 138 let excluded = match Jmap_email.Vacation.Vacation_response.id vacation with 139 | _ -> [] (* exclude_addresses not available in interface *) in 140 if excluded <> [] then ( 141 Printf.printf "\nExcluded addresses:\n"; 142 List.iter (Printf.printf " - %s\n") excluded 143 ) 144 145(* Get current vacation response *) 146let get_vacation_response ctx session account_id = 147 let get_args = Jmap.Methods.Get_args.v 148 ~account_id 149 ~properties:["isEnabled"; "subject"; "textBody"; "htmlBody"; 150 "fromDate"; "toDate"; "excludeAddresses"] 151 () in 152 153 let invocation = Jmap.Wire.Invocation.v 154 ~method_name:"VacationResponse/get" 155 ~arguments:(`Assoc []) (* Would serialize get_args *) 156 ~method_call_id:"get1" 157 () in 158 159 let request = Jmap.Wire.Request.v 160 ~using:[Jmap.capability_core; Jmap_email.capability_mail; Jmap_email.capability_vacationresponse] 161 ~method_calls:[invocation] 162 () in 163 164 match Jmap_unix.request ctx request with 165 | Ok _ -> 166 (* Would extract from response - for now create a sample *) 167 Ok (Jmap_email.Vacation.Vacation_response.v 168 ~id:"vacation1" 169 ~is_enabled:false 170 ~subject:"Out of Office" 171 ~text_body:"I am currently out of the office and will respond when I return." 172 ()) 173 | Error e -> Error e 174 175(* Update vacation response *) 176let update_vacation_response ctx session account_id vacation_id updates = 177 let update_map = Hashtbl.create 1 in 178 Hashtbl.add update_map vacation_id updates; 179 180 let set_args = Jmap.Methods.Set_args.v 181 ~account_id 182 ~update:update_map 183 () in 184 185 let invocation = Jmap.Wire.Invocation.v 186 ~method_name:"VacationResponse/set" 187 ~arguments:(`Assoc []) (* Would serialize set_args *) 188 ~method_call_id:"set1" 189 () in 190 191 let request = Jmap.Wire.Request.v 192 ~using:[Jmap.capability_core; Jmap_email.capability_mail; Jmap_email.capability_vacationresponse] 193 ~method_calls:[invocation] 194 () in 195 196 match Jmap_unix.request ctx request with 197 | Ok _ -> Ok () 198 | Error e -> Error e 199 200(* Process vacation action *) 201let process_vacation_action ctx session account_id action = 202 match action with 203 | Show -> 204 (match get_vacation_response ctx session account_id with 205 | Ok vacation -> 206 show_vacation_response vacation; 207 0 208 | Error e -> 209 Printf.eprintf "Failed to get vacation response: %s\n" (Jmap.Error.error_to_string e); 210 1) 211 212 | Enable config -> 213 Printf.printf "Enabling vacation response...\n"; 214 215 (* Build the vacation response object *) 216 let vacation = Jmap_email.Vacation.Vacation_response.v 217 ~id:"singleton" 218 ~is_enabled:true 219 ?subject:config.subject 220 ~text_body:config.text_body 221 ?html_body:config.html_body 222 ?from_date:config.from_date 223 ?to_date:config.to_date 224 () in 225 226 (match update_vacation_response ctx session account_id "singleton" vacation with 227 | Ok () -> 228 Printf.printf "\nVacation response enabled successfully!\n"; 229 230 (* Show what was set *) 231 show_vacation_response vacation; 232 0 233 | Error e -> 234 Printf.eprintf "Failed to enable vacation response: %s\n" (Jmap.Error.error_to_string e); 235 1) 236 237 | Disable -> 238 Printf.printf "Disabling vacation response...\n"; 239 240 let updates = Jmap_email.Vacation.Vacation_response.v 241 ~id:"singleton" 242 ~is_enabled:false 243 () in 244 245 (match update_vacation_response ctx session account_id "singleton" updates with 246 | Ok () -> 247 Printf.printf "Vacation response disabled successfully!\n"; 248 0 249 | Error e -> 250 Printf.eprintf "Failed to disable vacation response: %s\n" (Jmap.Error.error_to_string e); 251 1) 252 253 | Update config -> 254 Printf.printf "Updating vacation response...\n"; 255 256 (* Only update specified fields *) 257 let vacation = Jmap_email.Vacation.Vacation_response.v 258 ~id:"singleton" 259 ?subject:config.subject 260 ~text_body:config.text_body 261 ?html_body:config.html_body 262 ?from_date:config.from_date 263 ?to_date:config.to_date 264 () in 265 266 (match update_vacation_response ctx session account_id "singleton" vacation with 267 | Ok () -> 268 Printf.printf "Vacation response updated successfully!\n"; 269 270 (* Show current settings *) 271 (match get_vacation_response ctx session account_id with 272 | Ok current -> show_vacation_response current 273 | Error _ -> ()); 274 0 275 | Error e -> 276 Printf.eprintf "Failed to update vacation response: %s\n" (Jmap.Error.error_to_string e); 277 1) 278 279(* Command implementation *) 280let vacation_command host user password enable disable show subject message 281 message_file html_message from_date to_date exclude : int = 282 Printf.printf "JMAP Vacation Manager\n"; 283 Printf.printf "Server: %s\n" host; 284 Printf.printf "User: %s\n\n" user; 285 286 (* Determine action *) 287 let action_count = (if enable then 1 else 0) + 288 (if disable then 1 else 0) + 289 (if show then 1 else 0) in 290 291 if action_count = 0 then ( 292 Printf.eprintf "Error: Must specify an action: --enable, --disable, or --show\n"; 293 exit 1 294 ); 295 296 if action_count > 1 then ( 297 Printf.eprintf "Error: Can only specify one action at a time\n"; 298 exit 1 299 ); 300 301 (* Build vacation config if enabling or updating *) 302 let config = if enable || (not disable && not show) then 303 (* Read message content *) 304 let text_body = match message, message_file with 305 | Some text, _ -> text 306 | None, Some file -> read_file file 307 | None, None -> 308 if enable then ( 309 Printf.eprintf "Error: Must provide vacation message (--message or --message-file)\n"; 310 exit 1 311 ) else "" 312 in 313 314 (* Parse dates *) 315 let from_date = match from_date with 316 | Some date_str -> parse_date date_str 317 | None -> None 318 in 319 320 let to_date = match to_date with 321 | Some date_str -> parse_date date_str 322 | None -> None 323 in 324 325 Some { 326 subject; 327 text_body; 328 html_body = html_message; 329 from_date; 330 to_date; 331 exclude_addresses = exclude; 332 } 333 else 334 None 335 in 336 337 (* Determine action *) 338 let action = 339 if show then Show 340 else if disable then Disable 341 else if enable then Enable (Option.get config) 342 else Update (Option.get config) 343 in 344 345 (* Connect to server *) 346 let ctx = Jmap_unix.create_client () in 347 let result = Jmap_unix.quick_connect ~host ~username:user ~password in 348 349 let (ctx, session) = match result with 350 | Ok (ctx, session) -> (ctx, session) 351 | Error e -> 352 Printf.eprintf "Connection failed: %s\n" (Jmap.Error.error_to_string e); 353 exit 1 354 in 355 356 (* Check vacation capability *) 357 (* Note: has_capability not available in interface, assuming server supports it *) 358 359 (* Get the primary account ID *) 360 let account_id = match Jmap.get_primary_account session Jmap_email.capability_mail with 361 | Ok id -> id 362 | Error e -> 363 Printf.eprintf "No mail account found: %s\n" (Jmap.Error.error_to_string e); 364 exit 1 365 in 366 367 (* Process the action *) 368 process_vacation_action ctx session account_id action 369 370(* Command definition *) 371let vacation_cmd = 372 let doc = "manage vacation/out-of-office auto-responses" in 373 let man = [ 374 `S Manpage.s_description; 375 `P "Manage vacation responses (out-of-office auto-replies) via JMAP."; 376 `P "Configure automatic email responses for when you're away."; 377 `S Manpage.s_examples; 378 `P "Show current vacation settings:"; 379 `P " $(mname) -h jmap.example.com -u user@example.com -p secret123 --show"; 380 `P ""; 381 `P "Enable vacation response:"; 382 `P " $(mname) -h jmap.example.com -u user@example.com -p secret123 --enable \\"; 383 `P " --subject \"Out of Office\" \\"; 384 `P " --message \"I am currently out of the office and will return on Monday.\""; 385 `P ""; 386 `P "Enable with date range:"; 387 `P " $(mname) -h jmap.example.com -u user@example.com -p secret123 --enable \\"; 388 `P " --message-file vacation.txt \\"; 389 `P " --from-date 2024-07-01 --to-date 2024-07-15"; 390 `P ""; 391 `P "Disable vacation response:"; 392 `P " $(mname) -h jmap.example.com -u user@example.com -p secret123 --disable"; 393 ] in 394 395 let cmd = 396 Cmd.v 397 (Cmd.info "jmap-vacation-manager" ~version:"1.0" ~doc ~man) 398 Term.(const vacation_command $ host_arg $ user_arg $ password_arg $ 399 enable_arg $ disable_arg $ show_arg $ subject_arg $ message_arg $ 400 message_file_arg $ html_message_arg $ from_date_arg $ to_date_arg $ 401 exclude_arg) 402 in 403 cmd 404 405(* Main entry point *) 406let () = exit (Cmd.eval' vacation_cmd)