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,269 @@ 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 ecSuccess;
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 ecSuccess;
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+ ppropvals->emplace_back (tag, value);
703+ continue ;
704+ }
705+ auto erp = cu_alloc<uint32_t >();
706+ if (erp == nullptr )
707+ return false ;
708+ *erp = err;
709+ ppropvals->emplace_back (CHANGE_PROP_TYPE (PT_ERROR, tag), erp);
710+ }
711+ return true ;
712+ } catch (const std::bad_alloc &) {
713+ mlog (LV_ERR, " E-2227: ENOMEM" );
714+ return false ;
715+ }
716+
717+ static BOOL autoreply_setprop1 (const char *dir, const TAGGED_PROPVAL &pv)
718+ {
719+ auto path = autoreply_fspath (dir, pv.proptag );
720+
721+ switch (pv.proptag ) {
722+ case PR_EC_OUTOFOFFICE: {
723+ auto cfg = config_file_init (path.c_str (), oof_defaults);
724+ if (cfg == nullptr )
725+ return false ;
726+ auto v = *static_cast <const uint32_t *>(pv.pvalue );
727+ cfg->set_value (" OOF_STATE" , v == 1 ? " 1" : v == 2 ? " 2" : " 0" );
728+ return cfg->save ();
729+ }
730+ case PR_EC_OUTOFOFFICE_FROM:
731+ case PR_EC_OUTOFOFFICE_UNTIL: {
732+ auto cfg = config_file_init (path.c_str (), oof_defaults);
733+ if (cfg == nullptr )
734+ return false ;
735+ auto ts = rop_util_nttime_to_unix (*static_cast <const mapitime_t *>(pv.pvalue ));
736+ cfg->set_value (pv.proptag == PR_EC_OUTOFOFFICE_FROM ?
737+ " START_TIME" : " END_TIME" , std::to_string (ts).c_str ());
738+ return cfg->save ();
739+ }
740+ case PR_EC_OUTOFOFFICE_MSG:
741+ case PR_EC_EXTERNAL_REPLY: {
742+ wrapfd fd = open (path.c_str (), O_RDONLY);
743+ struct stat st{};
744+ ssize_t buff_len = 0 ;
745+ char *buf = nullptr ;
746+
747+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 ) {
748+ buff_len = strlen (static_cast <const char *>(pv.pvalue ));
749+ buf = cu_alloc<char >(buff_len + 256 );
750+ if (buf == nullptr )
751+ return false ;
752+ buff_len = gx_snprintf (buf, buff_len + 256 ,
753+ " Content-Type: text/html; charset=\" utf-8\"\r\n\r\n %s" ,
754+ static_cast <const char *>(pv.pvalue ));
755+ } else {
756+ buff_len = st.st_size ;
757+ buf = cu_alloc<char >(buff_len + strlen (static_cast <const char *>(pv.pvalue )) + 1 );
758+ if (buf == nullptr || read (fd.get (), buf, buff_len) != buff_len)
759+ return false ;
760+ buf[buff_len] = ' \0 ' ;
761+ auto token = strstr (buf, " \r\n\r\n " );
762+ if (token != nullptr ) {
763+ strcpy (&token[4 ], static_cast <const char *>(pv.pvalue ));
764+ buff_len = strlen (buf);
765+ } else {
766+ buff_len = sprintf (buf,
767+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n\r\n %s" ,
768+ static_cast <const char *>(pv.pvalue ));
769+ }
770+ }
771+ gromox::tmpfile tf;
772+ auto fdw = tf.open_linkable ((dir + " /config" s).c_str (), O_WRONLY, FMODE_PUBLIC);
773+ if (fdw < 0 || HXio_fullwrite (fdw, buf, buff_len) != buff_len)
774+ return false ;
775+ return tf.link_to (path.c_str ()) == 0 ;
776+ }
777+ case PR_EC_OUTOFOFFICE_SUBJECT:
778+ case PR_EC_EXTERNAL_SUBJECT: {
779+ wrapfd fd = open (path.c_str (), O_RDONLY);
780+ struct stat st{};
781+ ssize_t buff_len = 0 ;
782+ char *buf = nullptr ;
783+
784+ if (fd.get () < 0 || fstat (fd.get (), &st) != 0 ) {
785+ buff_len = strlen (static_cast <const char *>(pv.pvalue ));
786+ buf = cu_alloc<char >(buff_len + 256 );
787+ if (buf == nullptr )
788+ return false ;
789+ buff_len = sprintf (buf,
790+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s\r\n\r\n " ,
791+ static_cast <const char *>(pv.pvalue ));
792+ } else {
793+ buff_len = st.st_size ;
794+ buf = cu_alloc<char >(buff_len + strlen (static_cast <const char *>(pv.pvalue )) + 16 );
795+ if (buf == nullptr )
796+ return false ;
797+ auto token = cu_alloc<char >(buff_len + 1 );
798+ if (token == nullptr ||
799+ read (fd.get (), token, buff_len) != buff_len)
800+ return false ;
801+ token[buff_len] = ' \0 ' ;
802+ auto body = strstr (token, " \r\n\r\n " );
803+ if (body == nullptr )
804+ buff_len = sprintf (buf,
805+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s\r\n\r\n " ,
806+ static_cast <const char *>(pv.pvalue ));
807+ else
808+ buff_len = sprintf (buf,
809+ " Content-Type: text/html;\r\n\t charset=\" utf-8\"\r\n Subject: %s%s" ,
810+ static_cast <const char *>(pv.pvalue ), body);
811+ }
812+ gromox::tmpfile tf;
813+ auto fdw = tf.open_linkable ((dir + " /config" s).c_str (), O_WRONLY, FMODE_PUBLIC);
814+ if (fdw < 0 || HXio_fullwrite (fdw, buf, buff_len) != buff_len)
815+ return false ;
816+ return tf.link_to (path.c_str ()) == 0 ;
817+ }
818+ case PR_EC_ALLOW_EXTERNAL:
819+ case PR_EC_EXTERNAL_AUDIENCE: {
820+ auto cfg = config_file_init (path.c_str (), oof_defaults);
821+ if (cfg == nullptr )
822+ return false ;
823+ auto key = pv.proptag == PR_EC_ALLOW_EXTERNAL ?
824+ " ALLOW_EXTERNAL_OOF" : " EXTERNAL_AUDIENCE" ;
825+ cfg->set_value (key, *static_cast <const uint8_t *>(pv.pvalue ) ? " 1" : " 0" );
826+ return cfg->save ();
827+ }
828+ }
829+ return false ;
830+ }
831+
832+ BOOL exmdb_server::autoreply_setprop (const char *dir, cpid_t cpid,
833+ const TPROPVAL_ARRAY *ppropvals, PROBLEM_ARRAY *pproblems) try
834+ {
835+ for (const auto &p : *ppropvals)
836+ if (!autoreply_setprop1 (dir, p))
837+ return false ;
838+ return true ;
839+ } catch (const std::bad_alloc &) {
840+ mlog (LV_ERR, " E-2229: ENOMEM" );
841+ return false ;
842+ }
843+
572844BOOL exmdb_server::recalc_store_size (const char *dir, uint32_t flags)
573845{
574846 auto db = db_engine_get_db (dir);
0 commit comments