diff --git a/EcAlerter.h b/EcAlerter.h index e34d951..4801448 100644 --- a/EcAlerter.h +++ b/EcAlerter.h @@ -220,6 +220,13 @@ * NB. The value of the Subject field is used as a template * in the same way as the Replacement fields. *

+ *

The Threaded is just like the Email array except that + * it is only used for alarm messages, and the messages sent to the + * addresses in this array form a chain of linke messages referring + * back to each other rather than all being versions of the same + * message. This may give a better effect for people using mail + * clients which don't support the message-ID header well. + *

* *

Configuration of the alerter is done by the 'Alerter' key in the user * defaults system. The value for this key must be a dictionary configuring @@ -293,6 +300,7 @@ GSMimeSMTPClient *smtp; /** Client connection to MTA */ BOOL debug; /** Debug enabled in config */ BOOL supersede; /** If a clear should replace original */ + BOOL eThreaded; /** alarm reminder emails threaded */ } /** Called when user defaults are updated, this fetches the dictionary diff --git a/EcAlerter.m b/EcAlerter.m index 690b868..6e314a6 100644 --- a/EcAlerter.m +++ b/EcAlerter.m @@ -344,9 +344,9 @@ replaceFields(NSDictionary *fields, NSString *template) GSMimeDocument *doc; doc = AUTORELEASE([GSMimeDocument new]); - [doc setHeader: @"subject" value: subject parameters: nil]; - [doc setHeader: @"to" value: address parameters: nil]; - [doc setHeader: @"from" value: eFrom parameters: nil]; + [doc setHeader: @"Subject" value: subject parameters: nil]; + [doc setHeader: @"To" value: address parameters: nil]; + [doc setHeader: @"From" value: eFrom parameters: nil]; [doc setContent: text type: @"text/plain" name: nil]; [[self _smtp] send: doc]; } @@ -660,6 +660,80 @@ replaceFields(NSDictionary *fields, NSString *template) } NS_ENDHANDLER + NS_DURING + { + o = [d objectForKey: @"Threaded"]; + if ([o isKindOfClass: [NSString class]] == YES) + { + if ([o hasPrefix: @"("]) + { + o = [(NSString*)o propertyList]; + } + else + { + o = [NSArray arrayWithObject: o]; + } + } + if (o != nil) + { + NSString *s = [d objectForKey: @"Subject"]; + + if (reminder > 0) + { + NSString *emailIdentifier; + NSString *emailInReplyTo; + + if (1 == reminder) + { + emailInReplyTo = identifier; + emailIdentifier + = [identifier stringByAppendingString: @"_1"]; + } + else + { + emailInReplyTo + = [NSString stringWithFormat: @"%@_%d", + identifier, reminder - 1]; + emailIdentifier + = [NSString stringWithFormat: @"%@_%d", + identifier, reminder]; + } + + [m setObject: emailIdentifier + forKey: @"EmailIdentifier"]; + [m setObject: emailInReplyTo + forKey: @"EmailInReplyTo"]; + } + + if (s != nil) + { + [m setObject: s forKey: @"Subject"]; + } + + s = [d objectForKey: @"EmailReplacement"]; + if (nil == s) + { + s = [d objectForKey: @"Replacement"]; + if (nil == s) + { + /* Full details. */ + s = @"{Server}({Host}): {Timestamp} {Type}" + @" - {Message}"; + } + } + [m setObject: s forKey: @"Replacement"]; + [self mail: m identifier: identifier isClear: isClear to: o]; + } + } + NS_HANDLER + { + NSLog(@"Exception handling Email send for rule: %@", + localException); + } + NS_ENDHANDLER + [m removeObjectForKey: @"EmailIdentifier"]; + [m removeObjectForKey: @"EmailInReplyTo"]; + NS_DURING { o = [d objectForKey: @"Sms"]; @@ -904,15 +978,71 @@ replaceFields(NSDictionary *fields, NSString *template) [self _smtp]; doc = AUTORELEASE([GSMimeDocument new]); - [doc setHeader: @"subject" value: subject parameters: nil]; + [doc setHeader: @"Subject" value: subject parameters: nil]; [doc setContent: text type: @"text/plain" name: nil]; - [doc setHeader: @"from" value: eFrom parameters: nil]; + [doc setHeader: @"From" value: eFrom parameters: nil]; if ([identifier length] > 0) { NSString *mID; - mID = [NSString stringWithFormat: @"", identifier, eBase]; + /* This may reference an earlier email (for threaded display) + */ + mID = [m objectForKey: @"EmailInReplyTo"]; + if (nil != mID) + { + mID = [NSString stringWithFormat: @"", mID, eBase]; + [doc setHeader: @"In-Reply-To" value: mID parameters: nil]; + } + + /* We may have an identifier set in the dictionary to use + */ + mID = [m objectForKey: @"EmailIdentifier"]; + if (nil != mID) + { + NSRange r = [mID rangeOfString: @"_"]; + + if (r.length > 0) + { +#if 1 + int version; + + /* Reference all earlier messages in thread. + */ + version = [[mID substringFromIndex: NSMaxRange(r)] intValue]; + if (version > 1) + { + NSMutableString *ms = [NSMutableString string]; + int index; + + for (index = 1; index < version; index++) + { + if (index > 1) + { + [ms appendString: @" "]; + } + [ms appendFormat: @"", + mID, index, eBase]; + } + [doc setHeader: @"References" value: ms parameters: nil]; + } +#else + NSString *ref; + + /* Reference the original message at start of thread. + */ + ref = [NSString stringWithFormat: @"", + [mID substringToIndex: r.location], eBase]; + [doc setHeader: @"References" value: ref parameters: nil]; +#endif + } + mID = [NSString stringWithFormat: @"", mID, eBase]; + } + else + { + mID = [NSString stringWithFormat: @"", + identifier, eBase]; + } if (YES == isClear) { @@ -933,7 +1063,7 @@ replaceFields(NSDictionary *fields, NSString *template) [doc setHeader: @"Message-ID" value: mID parameters: nil]; } } - else if ([identifier length] > 0) + else { [doc setHeader: @"Message-ID" value: mID parameters: nil]; } @@ -946,7 +1076,7 @@ replaceFields(NSDictionary *fields, NSString *template) GSMimeDocument *msg; msg = AUTORELEASE([doc copy]); - [msg setHeader: @"to" value: d parameters: nil]; + [msg setHeader: @"To" value: d parameters: nil]; [smtp send: msg]; } NS_HANDLER diff --git a/EcControl.m b/EcControl.m index 657bc41..feaeb4e 100644 --- a/EcControl.m +++ b/EcControl.m @@ -426,7 +426,8 @@ static NSString* cmdWord(NSArray* a, unsigned int pos) - (NSString*) description { - return [NSString stringWithFormat: @"Control server\n%@\n%@", alerter, sink]; + return [NSString stringWithFormat: @"%@ running since %@\n%@\n%@", + [super description], [self ecStarted], alerter, sink]; } - (oneway void) domanage: (NSString*)name diff --git a/EcProcess.h b/EcProcess.h index e7ad891..5b66fe7 100644 --- a/EcProcess.h +++ b/EcProcess.h @@ -358,6 +358,11 @@ extern NSString* cmdVersion(NSString *ver); */ - (void) ecDoLock; +/** Return the timestamp at which this process started up (when the + * receiver was initialised). + */ +- (NSDate*) ecStarted; + /** Release a lock on the shared EcProcess after thread-safe updates to * process-wide variables. */ diff --git a/EcProcess.m b/EcProcess.m index c910bde..662e574 100644 --- a/EcProcess.m +++ b/EcProcess.m @@ -1305,7 +1305,7 @@ static NSString *noFiles = @"No log files to archive"; return cmdSignalled; } -- (NSDate*) cmdStarted +- (NSDate*) ecStarted { return started; } @@ -2788,7 +2788,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); else { [self cmdPrintf: @"\n%@ on %@ running since %@\n\n", - cmdLogName(), ecHostName(), [self cmdStarted]]; + cmdLogName(), ecHostName(), [self ecStarted]]; if (NO == [cmdDefs boolForKey: @"Memory"]) { @@ -2814,7 +2814,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); else { [self cmdPrintf: @"\n%@ on %@ running since %@\n", - cmdLogName(), ecHostName(), [self cmdStarted]]; + cmdLogName(), ecHostName(), [self ecStarted]]; if ([self cmdLastIP] != nil) { [self cmdPrintf: @"Last IP at %@\n", [self cmdLastIP]]; @@ -3005,6 +3005,8 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); EcProc = self; + started = RETAIN([dateClass date]); + pinfo = [NSProcessInfo processInfo]; mgr = [NSFileManager defaultManager]; prf = EC_DEFAULTS_PREFIX; @@ -3208,8 +3210,6 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval); } } - started = RETAIN([dateClass date]); - /* See if we have a name specified for this process. */ ASSIGN(cmdName, [cmdDefs stringForKey: @"ProgramName"]);