#include #include #include #include #include #include #include #include #include "pfq_backend.h" #include "pfq_service.h" #include "../pfqmessage.h" #include "../config.h" #define PF_VERSION_20 1 #define PF_VERSION_21 2 #define PF_VERSION_22 3 int pf_version; int CURQ; int NUMMSG_THREAD; char queue_path[BUF_SIZE]; char config_path[BUF_SIZE]; char pftools_path[BUF_SIZE]; char postconf_path[BUF_SIZE]; char postsuper_path[BUF_SIZE]; char postcat_path[BUF_SIZE]; int has_configpath; #define HEADER_FROM "From: " #define HEADER_TO "To: " #define HEADER_SUBJECT "Subject: " #define HEADER_FROM_LEN 6 #define HEADER_TO_LEN 4 #define HEADER_SUBJECT_LEN 9 #define ENVELOPE_FROM "sender: " #define ENVELOPE_TO "recipient: " #define ENVELOPE_FROM_LEN 8 #define ENVELOPE_TO_LEN 11 #define ENVELOPE_NULL_SENDER "Null envelope sender" char *q_names[]={ "deferred", "hold", "incoming", "active", "corrupt" }; struct pfb_conf_t pfb_conf; const char* pfb_id() { return "postfix 2.x"; } int pfb_apiversion() { return PFQ_API_VERSION; } const char* pfb_version() { return "1.3"; } struct pfb_conf_t *pfb_getconf() { return &pfb_conf; } struct msg_t* msg_from_id(const char* mid) { int i; for ( i=0; i= msg_max ) return -1; if ( dig_limit && ( time(NULL)-dig_start > dig_limit ) ) return -1; bdir = opendir ( basedir ); while ( bdir && NUMMSG_THREAD < msg_max && ( dir = readdir ( bdir ) ) ) { if ( dig_limit && ( time(NULL)-dig_start > dig_limit ) ) return -1; snprintf ( full_path, sizeof(full_path), "%s/%s", basedir, dir->d_name ); if ( fs_should_dig ( dir, full_path ) ) { dir_dig ( full_path ); } else if ( NUMMSG_THREAD < msg_max && fs_should_add ( dir, full_path ) ) { msg = &(my_queue[NUMMSG_THREAD]); memcpy ( msg->id, dir->d_name, sizeof(msg->id) ); snprintf ( msg->path, sizeof(msg->path), "%s/%s", basedir, dir->d_name ); msg->changed = strcmp( dir->d_name, ext_queue[NUMMSG_THREAD].id ); NUMMSG_THREAD++; } } if ( bdir ) closedir ( bdir ); return PFBE_OK; } int pfb_init() { pfb_conf.max_char = 200; strcpy ( pfb_conf.command_path, "" ); strcpy ( pfb_conf.config_path, "" ); return PFBE_OK; } int pfb_setup( struct msg_t *qptr1, struct be_msg_t *qptr2 ) { char pconf[BUF_SIZE]; char buf[BUF_SIZE]; FILE *p; msg_max = pfb_conf.msg_max; dig_limit = pfb_conf.scan_limit; ext_queue = qptr1; my_queue = qptr2; pf_version = -1; CURQ = 0; pfb_using_envelope = 0; pfb_caps = BECAPS_MSG_HOLD | BECAPS_MSG_DEL | BECAPS_MSG_REQUEUE | BECAPS_MSG_ENVELOPE; memset ( config_path, 0, sizeof(config_path) ); memset ( pftools_path, 0, sizeof(pftools_path) ); memset ( postconf_path, 0, sizeof(postconf_path) ); memset ( postsuper_path, 0, sizeof(postsuper_path) ); memset ( postcat_path, 0, sizeof(postcat_path) ); if ( strlen(pfb_conf.command_path) ) snprintf ( pftools_path, BUF_SIZE-1, "%s", pfb_conf.command_path ); if ( strlen(pfb_conf.config_path) ) { snprintf ( config_path, BUF_SIZE-1, "%s", pfb_conf.config_path ); has_configpath = 1; } if ( strlen(pfb_conf.version) ) { switch ( pfb_conf.version[0] ) { case '0': pf_version = PF_VERSION_20; break; case '1': pf_version = PF_VERSION_21; break; case '2': pf_version = PF_VERSION_22; break; } } // If -p is not specified, use commands without path if ( strlen ( pftools_path ) ) { snprintf ( postconf_path, BUF_SIZE, "%s/postconf", pftools_path ); snprintf ( postsuper_path, BUF_SIZE, "%s/postsuper", pftools_path ); snprintf ( postcat_path, BUF_SIZE, "%s/postcat", pftools_path ); } else { snprintf ( postconf_path, BUF_SIZE, "postconf" ); snprintf ( postsuper_path, BUF_SIZE, "postsuper" ); snprintf ( postcat_path, BUF_SIZE, "postcat" ); } if ( pf_version == -1 ) { // Version autodetect pf_version = PF_VERSION_22; has_configpath = strlen(config_path); if ( has_configpath ) snprintf ( pconf, BUF_SIZE, "%s -c %s -h mail_version 2> /dev/null", postconf_path, config_path ); else snprintf ( pconf, BUF_SIZE, "%s -h mail_version 2> /dev/null", postconf_path ); p = popen ( pconf, "r" ); if ( !p ) { syslog ( LOGLEVEL, "pfqueue postfix2 backend: cannot guess postfix version, using 2.2 as default" ); sprintf ( buf, "2.2" ); } else freadl ( p, buf, sizeof(buf) ); if ( !strncmp ( buf, "2.0", 3 ) ) pf_version = PF_VERSION_20; if ( !strncmp ( buf, "2.1", 3 ) ) pf_version = PF_VERSION_21; if ( !strncmp ( buf, "2.2", 3 ) ) pf_version = PF_VERSION_22; } if ( pf_version == -1 ) { syslog ( LOGLEVEL, "pfqueue postfix2 backend: cannot determine postfix version (is postfix installed?)" ); return PFBE_UNUSABLE; } // Look for queue_directory if ( has_configpath ) snprintf ( pconf, BUF_SIZE, "%s -c %s -h queue_directory 2> /dev/null", postconf_path, config_path ); else snprintf ( pconf, BUF_SIZE, "%s -h queue_directory 2> /dev/null", postconf_path ); p = popen ( pconf, "r" ); if ( !p ) { syslog ( LOGLEVEL, "pfqueue postfix2 backend: cannot use postconf to search queue_directory, command was: \"%s\"", pconf ); pclose ( p ); return PFBE_UNUSABLE; } if ( !freadl ( p, queue_path, sizeof(queue_path) ) ) { syslog ( LOGLEVEL, "pfqueue postfix2 backend: cannot use postconf to search queue_directory, command was: \"%s\"", pconf ); pclose ( p ); return PFBE_UNUSABLE; } pclose ( p ); return PFBE_OK; } int pfb_close() { return PFBE_OK; } int pfb_fill_queue() { char buf[BUF_SIZE]; NUMMSG_THREAD = 0; snprintf ( buf, sizeof(buf), "%s/%s", queue_path, q_names[CURQ] ); dir_dig(buf); return NUMMSG_THREAD; } int pfb_retr_headers( const char* msgid ) { FILE *p; char buf[BUF_SIZE]; int f1, f2, f3, l1, l2; char *s1, *s2; struct msg_t *msg; int dirty; msg = msg_from_id(msgid); if ( !msg ) return PFBE_MSGNOTEX; if ( msg->hcached ) return PFBE_MSGCACHED; if ( has_configpath ) snprintf ( buf, BUF_SIZE, "%s -c %s -q %s 2> /dev/null", postcat_path, config_path, msg->id ); else snprintf ( buf, BUF_SIZE, "%s -q %s 2> /dev/null", postcat_path, msg->id ); p = popen ( buf, "r" ); if ( !p ) { strcpy ( msg->from, PFBE_SERROR ); strcpy ( msg->to, PFBE_SERROR ); msg->hcached = 0; return PFBE_MSGNOTEX; } f1 = f2 = f3 = 0; dirty = 1; strcpy ( msg->from, PFBE_SNOTFOUND ); strcpy ( msg->to, PFBE_SNOTFOUND ); if ( pfb_using_envelope ) { l1 = ENVELOPE_FROM_LEN; l2 = ENVELOPE_TO_LEN; s1 = ENVELOPE_FROM; s2 = ENVELOPE_TO; } else { l1 = HEADER_FROM_LEN; l2 = HEADER_TO_LEN; s1 = HEADER_FROM; s2 = HEADER_TO; } while ( (f1==0||f2==0||f3==0) && freadl ( p, buf, BUF_SIZE ) ) { if ( !f1 && ( !strncmp ( buf, s1, l1 ) ) ) { memcpy ( msg->from, buf+l1, sizeof(msg->from) ); if ( !strlen(msg->from) ) strcpy ( msg->from, ENVELOPE_NULL_SENDER ); f1++; } if ( !f2 && ( !strncmp ( buf, s2, l2 ) ) ) { memcpy ( msg->to, buf+l2, sizeof(msg->to) ); f2++; } if ( !f3 && !strncmp ( buf, HEADER_SUBJECT, HEADER_SUBJECT_LEN ) ) { memcpy ( msg->subj, buf+HEADER_SUBJECT_LEN, sizeof(msg->subj) ); f3++; } } pclose ( p ); if ( f1 && f2 && f3 ) dirty = 0; if ( (!dirty) && strlen(msg->to) && strlen(msg->from) ) msg->hcached = 1; else msg->hcached = 0; return PFBE_OK; } int pfb_retr_status ( const char* msgid ) { FILE *p; char buf[BUF_SIZE]; char buf2[BUF_SIZE]; char *c; struct msg_t *msg; msg = msg_from_id(msgid); if ( !msg ) return PFBE_MSGNOTEX; if ( msg->scached ) return PFBE_MSGCACHED; if ( CURQ == Q_DEFERRED ) { // replace 'deferred' with 'defer' c = strstr ( msg->path, "deferred" ); if ( c ) { memset ( buf, 0, sizeof(buf) ); strncpy ( buf, msg->path, c - msg->path ); sprintf ( buf2, "%sdefer%s", buf, c+8 ); } p = fopen ( buf2, "r" ); if ( !p ) strcpy ( msg->stat, "Deferred, no reason" ); else { if ( pf_version > PF_VERSION_20 ) flookfor ( p, msg->stat, sizeof(msg->stat), "reason=" ); else freadl ( p, msg->stat, sizeof(msg->stat) ); } if ( p ) fclose ( p ); } else if ( CURQ == Q_ACTIVE ) strcpy ( msg->stat, "Active" ); else if ( CURQ == Q_HOLD ) strcpy ( msg->stat, "Held" ); else if ( CURQ == Q_INCOMING ) strcpy ( msg->stat, "Incoming" ); msg->scached = 1; return PFBE_OK; } int pfb_retr_body( const char* msgid, char *buffer, size_t buflen ) { char b[BUF_SIZE]; int j; FILE *p; struct msg_t *msg; msg = msg_from_id(msgid); if ( !msg ) return PFBE_MSGNOTEX; if ( has_configpath ) snprintf ( b, BUF_SIZE, "%s -c %s -q %s 2> /dev/null", postcat_path, config_path, msg->id ); else snprintf ( b, BUF_SIZE, "%s -q %s 2> /dev/null", postcat_path, msg->id ); p = popen ( b, "r" ); if ( !p ) return PFBE_MSGNOTEX; j = fread( buffer, sizeof(char), buflen, p ); pclose ( p ); return j; } int pfb_action(int act, const char* msg) { char b[BUF_SIZE]; char o; switch ( act ) { case MSG_DELETE: o = 'd'; break; case MSG_HOLD: o = 'h'; break; case MSG_RELEASE: o = 'H'; break; case MSG_REQUEUE: o = 'r'; break; default: return 1; } if ( has_configpath ) snprintf ( b, BUF_SIZE, "%s -c %s -%c %s 2>/dev/null", postsuper_path, config_path, o, msg ); else snprintf ( b, BUF_SIZE, "%s -%c %s 2>/dev/null", postsuper_path, o, msg ); system ( b ); return PFBE_OK; } int pfb_message_delete( const char* msg ) { return pfb_action ( MSG_DELETE, msg ); } int pfb_message_hold( const char* msg ) { return pfb_action ( MSG_HOLD, msg ); } int pfb_message_requeue( const char* msg ) { return pfb_action ( MSG_REQUEUE, msg ); } int pfb_message_release( const char* msg ) { return pfb_action ( MSG_RELEASE, msg ); } int pfb_set_queue ( int q ) { if ( q