2323#include < gromox/exmdb_common_util.hpp>
2424#include < gromox/exmdb_server.hpp>
2525#include < gromox/fileio.h>
26+ #include < gromox/config_file.hpp>
27+ #include < gromox/mail_func.hpp>
2628#include < gromox/mapi_types.hpp>
2729#include < gromox/mapidefs.h>
2830#include < gromox/mysql_adaptor.hpp>
@@ -43,6 +45,13 @@ struct sql_del {
4345
4446}
4547
48+ static constexpr cfg_directive oof_defaults[] = {
49+ {" allow_external_oof" , " 0" , CFG_BOOL},
50+ {" external_audience" , " 0" , CFG_BOOL},
51+ {" oof_state" , " 0" },
52+ CFG_TABLE_END,
53+ };
54+
4655BOOL exmdb_server::vacuum (const char *dir)
4756{
4857 return db_engine_vacuum (dir);
@@ -569,6 +578,278 @@ BOOL exmdb_server::autoreply_tsupdate(const char *dir, const char *peer) try
569578 return false ;
570579}
571580
581+ static std::string autoreply_fspath (const char *dir, proptag_t proptag)
582+ {
583+ switch (proptag) {
584+ case PR_EC_OUTOFOFFICE:
585+ case PR_EC_OUTOFOFFICE_FROM:
586+ case PR_EC_OUTOFOFFICE_UNTIL:
587+ case PR_EC_ALLOW_EXTERNAL:
588+ case PR_EC_EXTERNAL_AUDIENCE:
589+ return dir + " /config/autoreply.cfg" s;
590+ case PR_EC_OUTOFOFFICE_MSG:
591+ case PR_EC_OUTOFOFFICE_SUBJECT:
592+ return dir + " /config/internal-reply" s;
593+ case PR_EC_EXTERNAL_REPLY:
594+ case PR_EC_EXTERNAL_SUBJECT:
595+ return dir + " /config/external-reply" s;
596+ default :
597+ return {};
598+ }
599+ }
600+
601+ static ec_error_t autoreply_getprop1 (const char *dir,
602+ proptag_t proptag, void *&value)
603+ {
604+ char subject[1024 ];
605+ MIME_FIELD mime_field;
606+ auto path = autoreply_fspath (dir, proptag);
607+
608+ switch (proptag) {
609+ case PR_EC_OUTOFOFFICE: {
610+ auto oofstate = cu_alloc<uint32_t >();
611+ if (oofstate == nullptr )
612+ return ecServerOOM;
613+ auto cfg = config_file_init (path.c_str (), oof_defaults);
614+ *oofstate = cfg != nullptr ? cfg->get_ll (" oof_state" ) : 0 ;
615+ value = oofstate;
616+ return ecSuccess;
617+ }
618+ case PR_EC_OUTOFOFFICE_MSG:
619+ case PR_EC_EXTERNAL_REPLY: {
620+ struct stat st;
621+ wrapfd fd = open (path.c_str (), O_RDONLY);
622+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 )
623+ return ecNotFound;
624+ auto buf = cu_alloc<char >(st.st_size + 1 );
625+ if (buf == nullptr )
626+ return ecServerOOM;
627+ if (read (fd.get (), buf, st.st_size ) != st.st_size )
628+ return ecReadFault;
629+ buf[st.st_size ] = ' \0 ' ;
630+ auto ptr = strstr (buf, " \r\n\r\n " );
631+ value = ptr != nullptr ? &ptr[4 ] : nullptr ;
632+ return value != nullptr ? ecSuccess : ecNotFound;
633+ }
634+ case PR_EC_OUTOFOFFICE_SUBJECT:
635+ case PR_EC_EXTERNAL_SUBJECT: {
636+ struct stat st;
637+ wrapfd fd = open (path.c_str (), O_RDONLY);
638+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 )
639+ return ecNotFound;
640+ auto buf = cu_alloc<char >(st.st_size );
641+ if (buf == nullptr )
642+ return ecServerOOM;
643+ if (buf == nullptr || read (fd.get (), buf, st.st_size ) != st.st_size )
644+ return ecReadFault;
645+ size_t offset = 0 ;
646+ while (auto parsed = parse_mime_field (&buf[offset], st.st_size - offset, &mime_field)) {
647+ offset += parsed;
648+ if (strcasecmp (mime_field.name .c_str (), " Subject" ) == 0 &&
649+ mime_field.value .size () < sizeof (subject) &&
650+ mime_string_to_utf8 (" utf-8" , mime_field.value .c_str (), subject, sizeof (subject))) {
651+ value = common_util_dup (subject);
652+ return value != nullptr ? ecSuccess : ecServerOOM;
653+ }
654+ if (buf[offset] == ' \r ' && buf[offset+1 ] == ' \n ' )
655+ break ;
656+ }
657+ return ecNotFound;
658+ }
659+ case PR_EC_OUTOFOFFICE_FROM:
660+ case PR_EC_OUTOFOFFICE_UNTIL: {
661+ auto cfg = config_file_init (path.c_str (), oof_defaults);
662+ if (cfg == nullptr )
663+ return ecNotFound;
664+ auto ts = cu_alloc<mapitime_t >();
665+ if (ts == nullptr )
666+ return ecServerOOM;
667+ auto key = proptag == PR_EC_OUTOFOFFICE_FROM ? " START_TIME" : " END_TIME" ;
668+ auto sval = cfg->get_value (key);
669+ if (sval == nullptr )
670+ return ecNotFound;
671+ *ts = rop_util_unix_to_nttime (strtoll (sval, nullptr , 0 ));
672+ value = ts;
673+ return ecSuccess;
674+ }
675+ case PR_EC_ALLOW_EXTERNAL:
676+ case PR_EC_EXTERNAL_AUDIENCE: {
677+ static constexpr uint8_t fake_true = true , fake_false = false ;
678+ auto cfg = config_file_init (path.c_str (), oof_defaults);
679+ if (cfg == nullptr ) {
680+ value = deconst (&fake_false);
681+ return ecSuccess;
682+ }
683+ auto key = proptag == PR_EC_ALLOW_EXTERNAL ? " allow_external_oof" : " external_audience" ;
684+ value = deconst (cfg->get_ll (key) == 0 ? &fake_false : &fake_true);
685+ return ecSuccess;
686+ }
687+ }
688+ return ecNotFound;
689+ }
690+
691+ BOOL exmdb_server::autoreply_getprop (const char *dir, cpid_t cpid,
692+ const PROPTAG_ARRAY *pproptags, TPROPVAL_ARRAY *ppropvals) try
693+ {
694+ ppropvals->count = 0 ;
695+ ppropvals->ppropval = cu_alloc<TAGGED_PROPVAL>(pproptags->count );
696+ if (ppropvals->ppropval == nullptr )
697+ return false ;
698+ for (proptag_t tag : *pproptags) {
699+ void *value = nullptr ;
700+ auto err = autoreply_getprop1 (dir, tag, value);
701+ if (err == ecSuccess) {
702+ if (value != nullptr ) {
703+ ppropvals->emplace_back (tag, value);
704+ continue ;
705+ }
706+ err = ecNotFound;
707+ }
708+ auto erp = cu_alloc<uint32_t >();
709+ if (erp == nullptr )
710+ return false ;
711+ *erp = err;
712+ ppropvals->emplace_back (CHANGE_PROP_TYPE (PT_ERROR, tag), erp);
713+ }
714+ return true ;
715+ } catch (const std::bad_alloc &) {
716+ mlog (LV_ERR, " E-2227: ENOMEM" );
717+ return false ;
718+ }
719+
720+ static BOOL autoreply_setprop1 (const char *dir, const TAGGED_PROPVAL &pv)
721+ {
722+ auto path = autoreply_fspath (dir, pv.proptag );
723+
724+ /* Ensure file exists for the sake of config_file_init() */
725+ auto fdtest = open (path.c_str (), O_CREAT | O_WRONLY, FMODE_PUBLIC);
726+ if (fdtest < 0 )
727+ return false ;
728+ close (fdtest);
729+
730+ switch (pv.proptag ) {
731+ case PR_EC_OUTOFOFFICE: {
732+ auto cfg = config_file_init (path.c_str (), oof_defaults);
733+ if (cfg == nullptr )
734+ return false ;
735+ auto v = *static_cast <const uint32_t *>(pv.pvalue );
736+ cfg->set_value (" OOF_STATE" , v == 1 ? " 1" : v == 2 ? " 2" : " 0" );
737+ return cfg->save ();
738+ }
739+ case PR_EC_OUTOFOFFICE_FROM:
740+ case PR_EC_OUTOFOFFICE_UNTIL: {
741+ auto cfg = config_file_init (path.c_str (), oof_defaults);
742+ if (cfg == nullptr )
743+ return false ;
744+ auto ts = rop_util_nttime_to_unix (*static_cast <const mapitime_t *>(pv.pvalue ));
745+ cfg->set_value (pv.proptag == PR_EC_OUTOFOFFICE_FROM ?
746+ " START_TIME" : " END_TIME" , std::to_string (ts).c_str ());
747+ return cfg->save ();
748+ }
749+ case PR_EC_OUTOFOFFICE_MSG:
750+ case PR_EC_EXTERNAL_REPLY: {
751+ wrapfd fd = open (path.c_str (), O_RDONLY);
752+ struct stat st{};
753+ ssize_t buff_len = 0 ;
754+ char *buf = nullptr ;
755+
756+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 ) {
757+ buff_len = strlen (static_cast <const char *>(pv.pvalue ));
758+ buf = cu_alloc<char >(buff_len + 256 );
759+ if (buf == nullptr )
760+ return false ;
761+ buff_len = gx_snprintf (buf, buff_len + 256 ,
762+ " Content-Type: text/html; charset=\" utf-8\"\r\n\r\n %s" ,
763+ static_cast <const char *>(pv.pvalue ));
764+ } else {
765+ buff_len = st.st_size ;
766+ buf = cu_alloc<char >(buff_len + strlen (static_cast <const char *>(pv.pvalue )) + 1 );
767+ if (buf == nullptr || read (fd.get (), buf, buff_len) != buff_len)
768+ return false ;
769+ buf[buff_len] = ' \0 ' ;
770+ auto token = strstr (buf, " \r\n\r\n " );
771+ if (token != nullptr ) {
772+ strcpy (&token[4 ], static_cast <const char *>(pv.pvalue ));
773+ buff_len = strlen (buf);
774+ } else {
775+ buff_len = sprintf (buf,
776+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n\r\n %s" ,
777+ static_cast <const char *>(pv.pvalue ));
778+ }
779+ }
780+ gromox::tmpfile tf;
781+ auto fdw = tf.open_linkable ((dir + " /config" s).c_str (), O_WRONLY, FMODE_PUBLIC);
782+ if (fdw < 0 || HXio_fullwrite (fdw, buf, buff_len) != buff_len)
783+ return false ;
784+ return tf.link_to (path.c_str ()) == 0 ;
785+ }
786+ case PR_EC_OUTOFOFFICE_SUBJECT:
787+ case PR_EC_EXTERNAL_SUBJECT: {
788+ wrapfd fd = open (path.c_str (), O_RDONLY);
789+ struct stat st{};
790+ ssize_t buff_len = 0 ;
791+ char *buf = nullptr ;
792+
793+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 ) {
794+ buff_len = strlen (static_cast <const char *>(pv.pvalue ));
795+ buf = cu_alloc<char >(buff_len + 256 );
796+ if (buf == nullptr )
797+ return false ;
798+ buff_len = sprintf (buf,
799+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s\r\n\r\n " ,
800+ static_cast <const char *>(pv.pvalue ));
801+ } else {
802+ buff_len = st.st_size ;
803+ buf = cu_alloc<char >(buff_len + strlen (static_cast <const char *>(pv.pvalue )) + 16 );
804+ if (buf == nullptr )
805+ return false ;
806+ auto token = cu_alloc<char >(buff_len + 1 );
807+ if (token == nullptr ||
808+ read (fd.get (), token, buff_len) != buff_len)
809+ return false ;
810+ token[buff_len] = ' \0 ' ;
811+ auto body = strstr (token, " \r\n\r\n " );
812+ if (body == nullptr )
813+ buff_len = sprintf (buf,
814+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s\r\n\r\n " ,
815+ static_cast <const char *>(pv.pvalue ));
816+ else
817+ buff_len = sprintf (buf,
818+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s%s" ,
819+ static_cast <const char *>(pv.pvalue ), body);
820+ }
821+ gromox::tmpfile tf;
822+ auto fdw = tf.open_linkable ((dir + " /config" s).c_str (), O_WRONLY, FMODE_PUBLIC);
823+ if (fdw < 0 || HXio_fullwrite (fdw, buf, buff_len) != buff_len)
824+ return false ;
825+ return tf.link_to (path.c_str ()) == 0 ;
826+ }
827+ case PR_EC_ALLOW_EXTERNAL:
828+ case PR_EC_EXTERNAL_AUDIENCE: {
829+ auto cfg = config_file_init (path.c_str (), oof_defaults);
830+ if (cfg == nullptr )
831+ return false ;
832+ auto key = pv.proptag == PR_EC_ALLOW_EXTERNAL ?
833+ " ALLOW_EXTERNAL_OOF" : " EXTERNAL_AUDIENCE" ;
834+ cfg->set_value (key, *static_cast <const uint8_t *>(pv.pvalue ) ? " 1" : " 0" );
835+ return cfg->save ();
836+ }
837+ }
838+ return false ;
839+ }
840+
841+ BOOL exmdb_server::autoreply_setprop (const char *dir, cpid_t cpid,
842+ const TPROPVAL_ARRAY *ppropvals, PROBLEM_ARRAY *pproblems) try
843+ {
844+ for (const auto &p : *ppropvals)
845+ if (!autoreply_setprop1 (dir, p))
846+ return false ;
847+ return true ;
848+ } catch (const std::bad_alloc &) {
849+ mlog (LV_ERR, " E-2229: ENOMEM" );
850+ return false ;
851+ }
852+
572853BOOL exmdb_server::recalc_store_size (const char *dir, uint32_t flags)
573854{
574855 auto db = db_engine_get_db (dir);
0 commit comments