1515 */
1616
1717import { MessageData } from './ThreadData' ;
18+ import { SessionData } from './SessionData' ;
19+ import Mocks from './Mocks' ;
1820import Utils from './utils' ;
1921
2022const RE_FLAG_PATTERN = / ^ \/ ( .* ) \/ ( [ g i m u y s ] * ) $ / ;
2123
2224enum ConditionType {
23- AND , OR , NOT , SUBJECT , FROM , TO , CC , BCC , LIST , SENDER , RECEIVER , BODY ,
25+ AND , OR , NOT , SUBJECT , FROM , TO , CC , BCC , LIST , SENDER , RECEIVER , BODY , HEADER ,
2426}
2527
2628/**
@@ -65,7 +67,7 @@ export default class Condition {
6567 return pattern . replace ( / [ - \/ \\ ^ $ * + ? . ( ) | [ \] { } ] / g, '\\$&' ) ;
6668 }
6769
68- private static parseRegExp ( pattern : string , condition_str : string , matching_address : boolean ) : RegExp {
70+ public static parseRegExp ( pattern : string , condition_str : string , matching_address : boolean ) : RegExp {
6971 Utils . assert ( pattern . length > 0 , `Condition ${ condition_str } should have value but not found` ) ;
7072 const match = pattern . match ( RE_FLAG_PATTERN ) ;
7173 if ( match !== null ) {
@@ -85,6 +87,7 @@ export default class Condition {
8587 }
8688
8789 private readonly type : ConditionType ;
90+ private readonly subtype : string ;
8891 private readonly regexp : RegExp ;
8992 private readonly sub_conditions : Condition [ ] ;
9093
@@ -94,8 +97,9 @@ export default class Condition {
9497 `Condition ${ condition_str } should be surrounded by ().` ) ;
9598 const first_space = condition_str . indexOf ( " " ) ;
9699 const type_str = condition_str . substring ( 1 , first_space ) . trim ( ) . toUpperCase ( ) ;
97- const rest_str = condition_str . substring ( first_space + 1 , condition_str . length - 1 ) . trim ( ) ;
100+ let rest_str = condition_str . substring ( first_space + 1 , condition_str . length - 1 ) . trim ( ) ;
98101 this . type = ConditionType [ type_str as keyof typeof ConditionType ] ;
102+ this . subtype = "" ;
99103 switch ( this . type ) {
100104 case ConditionType . AND :
101105 case ConditionType . OR : {
@@ -119,6 +123,13 @@ export default class Condition {
119123 this . regexp = Condition . parseRegExp ( rest_str , condition_str , true ) ;
120124 break ;
121125 }
126+ case ConditionType . HEADER : {
127+ const subtype_first_space = rest_str . indexOf ( " " ) ;
128+ this . subtype = rest_str . substring ( 0 , subtype_first_space ) . trim ( ) ;
129+ rest_str = rest_str . substring ( subtype_first_space + 1 , rest_str . length - 1 ) . trim ( ) ;
130+ this . regexp = Condition . parseRegExp ( rest_str , condition_str , false ) ;
131+ break ;
132+ }
122133 case ConditionType . SUBJECT :
123134 case ConditionType . BODY : {
124135 this . regexp = Condition . parseRegExp ( rest_str , condition_str , false ) ;
@@ -177,6 +188,13 @@ export default class Condition {
177188 case ConditionType . BODY : {
178189 return this . regexp . test ( message_data . body ) ;
179190 }
191+ case ConditionType . HEADER : {
192+ const headerData = message_data . headers . get ( this . subtype ) ;
193+ if ( headerData !== undefined ) {
194+ return this . regexp . test ( headerData ) ;
195+ }
196+ return false ;
197+ }
180198 }
181199 }
182200
@@ -191,6 +209,17 @@ export default class Condition {
191209 return `(${ type_str } ${ regexp_str } ${ sub_str } )` ;
192210 }
193211
212+ getConditionHeaders ( ) : string [ ] {
213+ const headers = [ ] ;
214+ if ( this . type === ConditionType . HEADER ) {
215+ headers . push ( this . subtype ) ;
216+ }
217+ this . sub_conditions ?. forEach ( ( sub_condition ) => {
218+ headers . push ( ...sub_condition . getConditionHeaders ( ) ) ;
219+ } ) ;
220+ return headers ;
221+ }
222+
194223 public static testRegex ( it : Function , expect : Function ) {
195224
196225 function test_regexp ( condition_str : string , target_str : string , is_address : boolean ) {
@@ -271,11 +300,18 @@ export default class Condition {
271300 getSubject : ( ) => '' ,
272301 getPlainBody : ( ) => '' ,
273302 getRawContent : ( ) => '' ,
303+ getHeader : ( _name : string ) => '' ,
274304 } as GoogleAppsScript . Gmail . GmailMessage ;
275305
276- function test_cond ( condition_str : string , message : Partial < GoogleAppsScript . Gmail . GmailMessage > ) : boolean {
306+ function test_cond (
307+ condition_str : string ,
308+ message : Partial < GoogleAppsScript . Gmail . GmailMessage > ,
309+ session_data : Partial < SessionData > = { } ) : boolean {
277310 const condition = new Condition ( condition_str ) ;
278- const message_data = new MessageData ( Object . assign ( { } , base_message , message ) ) ;
311+ const mock_session_data = Mocks . getMockSessionData ( session_data ) ;
312+ const message_data = new MessageData (
313+ mock_session_data ,
314+ Object . assign ( { } , base_message , message ) ) ;
279315 return condition . match ( message_data ) ;
280316 }
281317
@@ -324,5 +360,56 @@ export default class Condition {
324360 getTo : ( ) => 'abc+Def@bar.com' ,
325361 } ) ) . toBe ( true )
326362 } )
363+
364+ it ( 'Matches custom header with value' , ( ) => {
365+ expect ( test_cond ( `(header Sender abc@def.com)` ,
366+ {
367+ getHeader : ( name : string ) => {
368+ if ( name === 'Sender' ) {
369+ return 'abc@def.com' ;
370+ }
371+ return '' ;
372+ } ,
373+ } ,
374+ {
375+ requested_headers : [ 'Sender' , 'List-Post' ] ,
376+ } ) ) . toBe ( true )
377+ } )
378+ it ( 'Matches nested custom header with value' , ( ) => {
379+ expect ( test_cond ( `(and
380+ (from abc@gmail.com)
381+ (and
382+ (header X-List mylist.gmail.com)
383+ (header Precedence /list/i)))` ,
384+ {
385+ getFrom : ( ) => 'DDD EEE <abc@gmail.com>' ,
386+ getHeader : ( name : string ) => {
387+ if ( name === 'X-List' ) {
388+ return 'mylist.gmail.com' ;
389+ }
390+ if ( name === 'Precedence' ) {
391+ return 'bills list' ;
392+ }
393+ return '' ;
394+ } ,
395+ } ,
396+ {
397+ requested_headers : [ 'X-List' , 'Precedence' ] ,
398+ } ) ) . toBe ( true )
399+ } )
400+ it ( 'Does not match custom header with incorrect data' , ( ) => {
401+ expect ( test_cond ( `(header MyHeader abc)` ,
402+ {
403+ getHeader : ( name : string ) => {
404+ if ( name === 'MyHeader' ) {
405+ return 'xyz' ;
406+ }
407+ return '' ;
408+ } ,
409+ } ,
410+ {
411+ requested_headers : [ 'MyHeader' ] ,
412+ } ) ) . toBe ( false )
413+ } )
327414 }
328415}
0 commit comments