pax_global_header00006660000000000000000000000064150212724170014513gustar00rootroot0000000000000052 comment=878fdba4c3c3f5982e183a9b144424025e94510d schleuder-5.0.1/000077500000000000000000000000001502127241700134745ustar00rootroot00000000000000schleuder-5.0.1/.gitattributes000066400000000000000000000001111502127241700163600ustar00rootroot00000000000000.gitlab-ci.yml export-ignore utils/ export-ignore .gitlab/ export-ignore schleuder-5.0.1/.gitignore000066400000000000000000000002131502127241700154600ustar00rootroot00000000000000.bundle vendor *.swp db/*.sqlite3 testmails/* .pc/ coverage/ /*.gem /*.gem.sig /*.tar.gz /*.tar.gz.sig Gemfile.lock spec/list-defaults.yml schleuder-5.0.1/.rspec000066400000000000000000000000361502127241700146100ustar00rootroot00000000000000--color --require spec_helper schleuder-5.0.1/.rubocop.yml000066400000000000000000000006341502127241700157510ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml AllCops: Exclude: - 'db/**/*' - 'vendor/**/*' - 'lib/schleuder/mail/gpg/sign_part.rb' Metrics/BlockLength: Max: 2700 Metrics/ClassLength: Max: 500 Style/StringLiterals: Enabled: true Layout/EmptyLineBetweenDefs: Enabled: true Layout/SpaceAfterComma: Enabled: true Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true schleuder-5.0.1/.rubocop_todo.yml000066400000000000000000000625471502127241700170110ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 0` # on 2023-11-17 09:59:24 UTC using RuboCop version 1.57.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 2 # Configuration parameters: Severity, Include. # Include: **/*.gemspec Gemspec/RubyVersionGlobalsUsage: Enabled: false # Offense count: 23 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: IndentationWidth. Layout/AssignmentIndentation: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleAlignWith, Severity. # SupportedStylesAlignWith: start_of_line, begin Layout/BeginEndAlignment: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleAlignWith. # SupportedStylesAlignWith: either, start_of_block, start_of_line Layout/BlockAlignment: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth. # SupportedStyles: case, end Layout/CaseIndentation: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Layout/ClosingParenthesisIndentation: Enabled: false # Offense count: 19 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterGuardClause: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLineAfterMagicComment: Enabled: false # Offense count: 29 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLines: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: around, only_before Layout/EmptyLinesAroundAccessModifier: Enabled: false # Offense count: 14 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, no_empty_lines Layout/EmptyLinesAroundBlockBody: Enabled: false # Offense count: 16 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only Layout/EmptyLinesAroundClassBody: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLinesAroundExceptionHandlingKeywords: Enabled: false # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). Layout/EmptyLinesAroundMethodBody: Enabled: false # Offense count: 12 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Layout/EmptyLinesAroundModuleBody: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleAlignWith, Severity. # SupportedStylesAlignWith: keyword, variable, start_of_line Layout/EndAlignment: Enabled: false # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. Layout/ExtraSpacing: Enabled: false # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses Layout/FirstArgumentIndentation: Enabled: false # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_brackets Layout/FirstArrayElementIndentation: Enabled: false # Offense count: 27 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces Layout/FirstHashElementIndentation: Enabled: false # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Layout/HeredocIndentation: Enabled: false # Offense count: 18 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Width, AllowedPatterns. Layout/IndentationWidth: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment. Layout/LeadingCommentSpace: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line Layout/MultilineArrayBraceLayout: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: symmetrical, new_line, same_line Layout/MultilineMethodCallBraceLayout: Enabled: false # Offense count: 21 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: aligned, indented Layout/MultilineOperationIndentation: Enabled: false # Offense count: 88 # This cop supports safe autocorrection (--autocorrect). Layout/SpaceAfterNot: Enabled: false # Offense count: 37 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceAroundEqualsInParameterDefault: Enabled: false # Offense count: 11 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator. # SupportedStylesForExponentOperator: space, no_space Layout/SpaceAroundOperators: Enabled: false # Offense count: 22 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceBeforeBlockBraces: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_no_space, require_space Layout/SpaceInLambdaLiteral: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBrackets: space, no_space Layout/SpaceInsideArrayLiteralBrackets: Enabled: false # Offense count: 18 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideBlockBraces: Enabled: false # Offense count: 255 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Enabled: false # Offense count: 13 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: space, compact, no_space Layout/SpaceInsideParens: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. # SupportedStyles: space, no_space # SupportedStylesForEmptyBrackets: space, no_space Layout/SpaceInsideReferenceBrackets: Enabled: false # Offense count: 29 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line Layout/TrailingEmptyLines: Enabled: false # Offense count: 87 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowInHeredoc. Layout/TrailingWhitespace: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods, AllowedPatterns. Lint/AmbiguousBlockAssociation: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/AmbiguousOperator: Enabled: false # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Enabled: false # Offense count: 2 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Lint/DeprecatedClassMethods: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Lint/DeprecatedOpenSSLConstant: Enabled: false # Offense count: 1 Lint/IneffectiveAccessModifier: Enabled: false # Offense count: 2 # Configuration parameters: AllowedParentClasses. Lint/MissingSuper: Enabled: false # Offense count: 1 # Configuration parameters: AllowedMethods, AllowedPatterns. Lint/NestedMethodDefinition: Enabled: false # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/NonDeterministicRequireOrder: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Lint/RedundantStringCoercion: Enabled: false # Offense count: 2 Lint/ShadowingOuterLocalVariable: Enabled: false # Offense count: 96 # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Enabled: false # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Enabled: false # Offense count: 13 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. Lint/UnusedMethodArgument: Enabled: false # Offense count: 80 # This cop supports unsafe autocorrection (--autocorrect-all). Lint/UselessAssignment: Enabled: false # Offense count: 56 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. Metrics/AbcSize: Max: 77 # Offense count: 4 # Configuration parameters: CountBlocks. Metrics/BlockNesting: Max: 4 # Offense count: 12 # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: Max: 495 # Offense count: 22 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/CyclomaticComplexity: Max: 34 # Offense count: 86 # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Max: 65 # Offense count: 2 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: Max: 178 # Offense count: 2 # Configuration parameters: CountKeywordArgs. Metrics/ParameterLists: Max: 6 MaxOptionalParameters: 4 # Offense count: 19 # Configuration parameters: AllowedMethods, AllowedPatterns. Metrics/PerceivedComplexity: Max: 34 # Offense count: 5 Naming/AccessorMethodName: Enabled: false # Offense count: 1 # Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms. # CheckDefinitionPathHierarchyRoots: lib, spec, test, src # AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS Naming/FileName: Enabled: false # Offense count: 1 # Configuration parameters: ForbiddenDelimiters. # ForbiddenDelimiters: (?i-mx:(^|\s)(EO[A-Z]{1}|END)(\s|$)) Naming/HeredocDelimiterNaming: Enabled: false # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Enabled: false # Offense count: 1 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Enabled: false # Offense count: 3 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros. # NamePrefix: is_, has_, have_ # ForbiddenPrefixes: is_, has_, have_ # AllowedMethods: is_a? # MethodDefinitionMacros: define_method, define_singleton_method Naming/PredicateName: Enabled: false # Offense count: 18 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: PreferredName. Naming/RescuedExceptionsVariableName: Enabled: false # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Security/YAMLLoad: Enabled: false # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: prefer_alias, prefer_alias_method Style/Alias: Enabled: false # Offense count: 12 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch # AllowedMethods: lambda, proc, it Style/BlockDelimiters: Enabled: false # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: is_a?, kind_of? Style/ClassCheck: Enabled: false # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Enabled: false # Offense count: 97 # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). Style/EachWithObject: Enabled: false # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, AllowComments. # SupportedStyles: empty, nil, both Style/EmptyElse: Enabled: false # Offense count: 9 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). Style/Encoding: Enabled: false # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). Style/ExpandPathArguments: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: format, sprintf, percent Style/FormatString: Enabled: false # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: EnforcedStyle: unannotated # Offense count: 154 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). Style/GlobalStdStream: Enabled: false # Offense count: 1 # Configuration parameters: AllowedVariables. Style/GlobalVars: Enabled: false # Offense count: 46 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Enabled: false # Offense count: 2 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowedReceivers. # AllowedReceivers: Thread.current Style/HashEachMethods: Enabled: false # Offense count: 18 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys # SupportedShorthandSyntax: always, never, either, consistent Style/HashSyntax: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowIfModifier. Style/IfInsideElse: Enabled: false # Offense count: 117 # This cop supports safe autocorrection (--autocorrect). Style/IfUnlessModifier: Enabled: false # Offense count: 136 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedMethods, AllowedPatterns. Style/MethodCallWithoutArgsParentheses: Enabled: false # Offense count: 1 Style/MixinUsage: Enabled: false # Offense count: 17 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false # Offense count: 49 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: both, prefix, postfix Style/NegatedIf: Enabled: false # Offense count: 4 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinBodyLength. # SupportedStyles: skip_modifier_ifs, always Style/Next: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/Not: Enabled: false # Offense count: 3 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedOctalStyle. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: Strict, AllowedNumbers, AllowedPatterns. Style/NumericLiterals: MinDigits: 6 # Offense count: 18 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle, AllowedMethods, AllowedPatterns. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false # Offense count: 6 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Enabled: false # Offense count: 5 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Enabled: false # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). Style/PerlBackrefs: Enabled: false # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: short, verbose Style/PreferredHashMethods: Enabled: false # Offense count: 17 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowedCompactTypes. # SupportedStyles: compact, exploded Style/RaiseArgs: EnforcedStyle: compact # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). Style/RedundantBegin: Enabled: false # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). Style/RedundantFileExtensionInRequire: Enabled: false # Offense count: 23 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowMultipleReturnValues. Style/RedundantReturn: Enabled: false # Offense count: 114 # This cop supports safe autocorrection (--autocorrect). Style/RedundantSelf: Enabled: false # Offense count: 43 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, AllowInnerSlashes. # SupportedStyles: slashes, percent_r, mixed Style/RegexpLiteral: Enabled: false # Offense count: 16 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit Style/RescueStandardError: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Enabled: false # Offense count: 2 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowModifier. Style/SoleNestedConditional: Enabled: false # Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: RequireEnglish, EnforcedStyle. # SupportedStyles: use_perl_names, use_english_names, use_builtin_english_names Style/SpecialGlobalVars: Enabled: false # Offense count: 9 # This cop supports safe autocorrection (--autocorrect). Style/StderrPuts: Enabled: false # Offense count: 9 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: Mode. Style/StringConcatenation: Enabled: false # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: MinSize. # SupportedStyles: percent, brackets Style/SymbolArray: EnforcedStyle: brackets # Offense count: 3 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments. # AllowedMethods: define_method Style/SymbolProc: Enabled: false # Offense count: 6 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArguments: Enabled: false # Offense count: 8 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInArrayLiteral: Enabled: false # Offense count: 7 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInHashLiteral: Enabled: false # Offense count: 10 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowNamedUnderscoreVariables. Style/TrailingUnderscoreVariable: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. # AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Enabled: false # Offense count: 1 # This cop supports safe autocorrection (--autocorrect). Style/WhileUntilModifier: Enabled: false # Offense count: 31 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: EnforcedStyle, MinSize, WordRegex. # SupportedStyles: percent, brackets Style/WordArray: Enabled: false # Offense count: 1 # This cop supports unsafe autocorrection (--autocorrect-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only Style/YodaCondition: Enabled: false # Offense count: 7 # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: Enabled: false # Offense count: 206 # This cop supports safe autocorrection (--autocorrect). # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. # URISchemes: http, https Layout/LineLength: Max: 434 schleuder-5.0.1/CHANGELOG.md000066400000000000000000001021031502127241700153020ustar00rootroot00000000000000Change Log ========== This project adheres to [Semantic Versioning](http://semver.org/). ## [5.0.1] / 2025-06-08 ### Fixed * The removal of the GnuPG homedir is more reliable. Before, code might have run into issues due to time-of-check-to-time-of-use problems. * Fixed code that rescues from failed message delivery to subscribers (and one more occurrence) (#538). * Since ActiveRecord >= 7.1, the SQLite3 connection adapter relies on `Write-Ahead Logging` (`WAL`), by default, which significantly enhances read and write performance. Such performance gains aren't of concern in Schleuder environments. Besides, Postfix only supports the `delete` journal mode, which Schleuder now enforces. (#541) ## [5.0.0] / 2024-11-13 ### Changed * Require Ruby 3.1 or later, drop support for all earlier versions. * Drop the default SKS keyserver. You can still specify your own, but by default now only keys.openpgp.org will be used to fetch keys by email address. * The default umask now is `0077`, allowing access to newly created files and directories only for the owner. It is configurable in `schleuder.yml`. * Don't suppress gpg's warnings about permissions and insecure memory, but log them in case they occur. (#496) * Drop using dirmngr, use custom code to fetch keys from keyservers. * Upgrade Active Record to version 7.1. * Upgrade mail to 2.8.1. * Check email address for `receive_from_subscribed_emailaddresses_only` in lower case letters to avoid wrong rejections. * `mail-gpg` is no longer a dependency. We included the relevant parts of its code into our own code base to avoid problems with surprising decisions from upstream. * The API now returns a detailed list of imported keys, so users can get earlier hints if the uploaded key e.g. was expired. * In reply to a x-keyword, if the signing key is used by multiple subscriptions, in order to find the address to reply to, Schleuder now looks for a subscription that has an email matching the incoming From-header. As a fallback the first subscription using the siging key is used (as before). ### Added * Optionally import selected keys from Autocrypt-headers and attachments of incoming emails, configurable for each list. See the code comment in `list-defaults.yml` for details. * The umask is configurable in `schleuder.yml`. * New dependency: libcurl and gem "typhoeus" (for networking). * Lookup keys on a VKS keyserver (e.g. keys.openpgp.org) before the SKS keyserver. * Configure a proxy to make HTTP requests (e.g. when fetching keys) through. This supports socks5(h) in order to route traffic through TOR. ### Fixed * Fixed sending the list's key to all subscribers if `deliver_selfsent` is false. * Fixed importing attached keys from some emails (like e.g. Thunderbird sends them). * Fixed the `From:` header of notifications sent to the `superadmin` to ensure a fully qualified domain name is used. * Fixed responding with an error message if an email contained only `x-add-key` but no other content. * Improved parsing the arguments for `x-subscribe`: now unpexected values should lead to an error message instead of being interpreted strangely. * Fixed not to collect keywords from emails if the email doesn't start with a keyword. ## [4.0.3] / 2022-04-12 ### Changed * Minor improvements of the German locales. ### Fixed * Since ActiveRecord >= 6.0, the SQLite3 connection adapter relies on boolean serialization to use 1 and 0, but does not natively recognize 't' and 'f' as booleans were previously serialized. Accordingly, handle conversion via a database migration of both column defaults and stored data provided by a user. (#505) * Similar, due to ActiveRecord >= 6.0, it seems it's necessary to handle limits of string columns explicitly. Accordingly, handle this via a migration to add these limits to the relevant columns. This has been an upstream issue for quite some time, see https://github.com/rails/rails/issues/19001 for details. * Fixed bug that circumvented filters when `bounces_drop_all` was set. (#508) ## [4.0.2] / 2021-07-31 ### Fixed * Fixed the verification of "encapsulated" PGP/MIME-messages as sent e.g. by KMail. ### Changed * Remove the dependency on bigdecimal, since updating activerecord we don't need it anymore. * Removed the dependency on the executables `hostname` and `whoami`. ## [4.0.1] / 2021-05-18 ### Fixed * `x-add-key` is able to handle attached binary key material. (#495) ## [4.0.0] / 2021-03-04 ### Added * Mandatory blank line: To separate keywords from email content, you *must* now insert a blank line between them. * Provide systemd configs for weekly key maintenance. This relies on a working systemd-timesyncd. (#422) * Support for Ruby 3.0. ### Changed * Keyword arguments are now also looked for in the following lines, until a blank line or a new keyword-line is encountered. * Allow to use the latest version of the gem `mail-gpg`. Our specs had been failing with versions > 0.4.2, but we found out it was our specs' fault. * Change the way we force gpg to never-ever interactively ask for a passphrase. This should fix problems with specific combinations of GnuPG and GPGME. * Drop support for Ruby 2.1, 2.2, 2.3 and 2.4, require Ruby 2.5. * Drop support for GPG 2.0, require GPG 2.2. * Drop support to migrate lists from version 2. This includes pin_keys code, which looked for subscriptions without an associated key, and tried to find a distinctly matching key. Originally, this was implemented to help with a shortcoming of code which handled version 2 to version 3 migration. (#411) * "Plugins" are now called "keyword handlers", and they are implemented differently. If you use custom plugins you have to rewrite them (see an included keyword handler for implementation hints, it's rather simple). If you don't, this change doesn't affect you. One positive effect of this: if a message contains an unknown keyword, no keyword is being handled but the sender is sent an error message; thus we avoid half-handled messages. * The key-attribute `oneline` has been renamed to `summary`. This affects also the http API. * Allow only fingerprints as argument to X-DELETE-KEY. We want to reference keys only by fingerprint, if possible (as we do with other keywords already). * Drop deprecated X-LISTNAME keyword. (#374) * Downcase email addresses: Email addresses are downcased before saving. * Update the dependency 'activerecord' to version 6.1. * Update the dependency 'factory_bot' to version 6.0. * Update the dependency 'sqlite3' to version 1.4. * Update the dependency 'database_cleaner' to version 2.0. ## [3.6.0] / 2021-02-07 ### Added * List Option `set_reply_to_to_sender` (default: false): When enabled, the `Reply-To`-header of the emails sent from Schleuder will be set to the original sender's `Reply-To`-header. If the original sender did not supply a `Reply-To`-header, the original `From`-header will be used. (#298) * List Option `munged_from` (default: false): When enabled, the `From`-header of the emails sent from Schleuder will contain the original sender's `From`-header included in the display name. To avoid DMARC issues, the `From`-header will stay the list's address. This results in a `From`-header such as: `"sender@sender.org via list@list.org" `. ### Fixed * Improve detection of bounces and thus fixing issues with falsely detected automatic messages. (#441) * Properly validate email addresses for subscriptions. (#483 & #484) ## [3.5.3] / 2020-06-13 ### Fixed * Fix running specs on IPv6-only machines. (#472) ## [3.5.2] / 2020-06-09 ### Fixed * `x-add-key` is able to handle inline key material, followed by non-key material, like a signature included in the body. (#470) ## [3.5.1] / 2020-04-15 ### Fixed * `x-add-key` is able to handle mails with attached, quoted-printable encoded keys. Such mails might be produced by Thunderbird. (#467) ## [3.5.0] / 2020-03-30 ### Added * New option for lists to include their public keys in the headers of outgoing emails (conforming with Autocrypt, https://autocrypt.org/). Defaults to true. (#335) * Add visual separator (78 dashes) to the end of the 'pseudoheaders' block: This should help users of Apple Mail, which jams this block and the body together. Hopefully, this change makes it easier to dinstiguish both parts from each other. (#348) * `deliver_selfsent` per-list option to control whether subscribers get a copy of mail they sent themselves. (#365) * Wrap pseudo headers if longer than 78 characters. ### Fixed * Ensure UTF-8 as external encoding, convert any non-utf8 email to utf-8 or drop invalid characters. This should ensure that plain text emails in different charsets can be parsed (#409, #458, #460). Also we apply that conversion to the first text part after we parsed it for keywords, if no charset is set. This fixes #457. These changes introduce a new dependency `charlock_holmes`. * Allow Jenkins job notifications to reach lists. Before, such mails were rejected due to being "auto-submitted". * Do not recognize sudo messages as automated message. (#248) * Fixed using x-attach-listkey with emails from Thunderbird that include protected headers. * Handle incoming mails encrypted to an absent key, using symmetric encryption or containing PGP-garbage in a more graceful manner: Don't throw an exception, don't notify (and annoy) the admins, instead inform the sender of the mail how to do better. (#337) * Add missing List-Id header to notification mails sent to admins. This should help with filtering such messages, which is currently not easy to do in a reliable way. * Fix running Schleuder with ruby 2.7. * Ensure that GnuPG never asks for a passphrase, even if it wants one. (#448) * Be more precise about how many keys are in the keyring and how many are usable, when resending. (#429) * Make it more clear what happens when resending an encrypted email fails (due to missing or too many matching keys), but falling back to unencrypted resend is allowed. (#343) * Be more explicit that resending to other CC recipients has been aborted. (#265) ## [3.4.1] / 2019-09-16 ### Fixed * Do not crash on protected header emails generated by mutt (#430) * Show an error message if `refresh_keys` is called with an email address for which no list exists. * Fix recognizing keywords with "protected headers" and empty subject. Previously, if the subject was unset, keywords were not recognized and the original "protected headers" could leak. (#431) ### Changed * Filter third party signatures on user-IDs when fetching or refreshing keys to mitigate against signature flooding. This works only if the version of gpg is 2.1.15 or newer. If the version is older, an email is being sent to the superadmin each time a key is fetched or keys are refreshed. See for background information. ## [3.4.0] / 2019-02-14 ### Fixed * Stop leaking keywords to third parties by stripping HTML from multipart/alternative messages if they contain keywords. (#399) * Avoid shelling out in a test-case to avoid an occasional error occurring in CI runs that complains about invalid data in ASCII-8BIT strings. ### Changed * Update the dependency 'mail' to version 2.7.x., and allow carriage returns (CR) in test-cases as mail-2.7 puts those out. * Update the dependency 'sqlite3' to version 1.3.x. * Adapt fixtures and factories for factorybot version 5.x. * Let schleuder-code load the filter files in test-mode, avoid explicit path names (which make headaches when running tests on installed packages). ## [3.3.0] / 2018-09-04 ### Fixed * Handle missing arguments for several keywords and reply with a helpful error-message. * Send replies to keyword-usage and notices to admins regardless of the delivery-flag of their subscription. (#354) * X-UNSUBSCRIBE will refuse to unsubscribe the last admin of a list. (#357) * Handle "protected subjects" in a way that Thunderbird/Enigmail recognize. (#74) * X-SET-FINGERPRINT will not anymore allow setting an empty fingerprint. (#360) ### Added * To remove a fingerprint from a subscription one can use the new keyword X-UNSET-FINGERPRINT (#360). * Extend the pseudoheaders configuration option to support 'sig' and 'enc' as configurable and sortable fields. ### Changed * The output of the keywords 'X-ADD-KEY' and 'X-DELETE-KEY' now also show the "oneline"-format to represent keys (which includes fingerprint, primary email-address, date of generation and possible expiry). (#295) * In the response to 'X-ADD-KEY', differentiate between 'newly imported' and 'updated' keys. * Parse keywords up to the first line detected as mail content, this addresses a first part of #249. ## [3.2.3] / 2018-05-14 ### Fixed * `X-SUBSCRIBE` now in all cases correctly sets the values for admin and delivery_enabled, if they are given as third and fourth argument, respectively. * To identify broken Microsoft Exchange messages, check if the headers include 'X-MS-Exchange' instead of specific domain names. Before this, we've missed mails sent by Exchange installations not operated by Microsoft or mails with a different "originating organisation domain" than Hotmail or Outlook. (#333) * Do not anymore fail on emails containing any PGP boundaries as part of their plain text. As a sideeffect we will not anymore validate an email a second time. Hence, a message part containing an additional signature within an encrypted (and possibly signed) email won't be validated and removed. (#261) * Exit with code 1 if a CLI-subcommand was not found (#339). * Fix finding keywords in request-messages that were sent from Thunderbird/Enigmail with enabled "protected subject". * Fix leaking the "protected subject" sent from Thunderbird/Enigmail. * Error messages are converted into human readable text now, instead of giving their class-name. (#338) * Require mail-gpg >= 0.3.3, which fixes a bug that let some equal-signs disappear under specific circumstances. (#287) ### Known issues * With the current used mail library version schleuder uses, there are certain malformed emails that can't be parsed. See #334 for background. This will be fixed in future releases of the mail library. ### Added * Enable to load external filters, similar to how we allow external plugins. (#282) ### Changed * Use schleuder.org as website and team@schleuder.org as contact email. * Check environment variable if code coverage check should be executed. (#342) * Transform GPG fingerprints to upper case before saving to database. (#327) * CLI-commands that (potentially) change data now remind the system admin to check file system permission if the command was run with root privileges. (#326) ## [3.2.2] / 2018-02-06 ### Changed * Temporarily depend on the ruby-library "mail" version 2.6. 2.7.0 seems to be a rough release (broke 8bit-characters, changed newline-styles) that needs to be ironed out before we can use it. * Changed wording of error-message in case of a missing or incorrect "X-LIST-NAME"-keyword. (Thanks, anarcat!) * Keys are now shuffled before refreshing them. This randomizes the way how we are querying keyservers for updated keys to avoid fingerprinting of a list's keyring. * Be more robust when dirmngr fails while refreshing keys, especially when updating over an onion service. Fixes #309. ### Fixed * Fix handling of emails with large first mime parts. We removed the code that limited the parsing of keywords to the first 1000 lines, as that broke the handling of certain large emails. * Fix output of Keys with a broken character set. This mainly affected schleuder-api. * Exit install-script if setting up the database failed. * Reveal less errors to public, and improve messages to admins. Previously errors about list-config etc. would have been included in bounces to the sender of the incoming email. Now only the admins get to know the details (which now also include the list the error happened with). Email-bounces only tell about a fatal error — except if the list could not be found, that information is still sent to the sender of the incoming email. * Make sure dirmngr is killed for a list after refreshing a list's keyring. Avoids servers getting memory exhausted. Fixes #289 * Fixed the API-daemon's interpretation of listnames that start with a number (previously the listname "1list" caused errors because it was taken as the integer 1). ## [3.2.1] / 2017-10-24 ### Changed * Explicitly depend on the latest version of ruby-gpgme (2.0.13) to force existing setups to update. This fixes the problem where unusable keys were not identified as such. (Previous versions of ruby-gpgme failed to properly provide the capabilities of a key.) ## [3.2.0] / 2017-10-23 ### Added * Internal footer: to be appended to each email that is sent to a subscribed address. Will not be included in messages to non-subscribed addresses. This change requires a change to the database, don't forget to run `schleuder install` after updating the code. * Optionally use an OS-wide defined keyserver by configuring a blank value for the keyserver. * Added keywords `X-RESEND-UNENCRYPTED` and `X-RESEND-CC-UNENCRYPTED` to enforce outgoing email(s) in cleartext regardless of whether we would find a key for the recipient or not. ### Changed * Public footer: Whitespace is not anymore stripped from the value of public_footer. * The API does not include anymore each key's key-data in response to `/keys.json`. This avoids performance problems with even medium sized keyrings. * The short representation of GnuPG keys became more human-friendly. Besides the fingerprint we now show the email-address of the first UID, the generation-date, and optionally the expiration-date. * Log the full exception when sending a message fails. (Thanks, Lunar!) * When creating a new list, we do not anymore look for a matching key for the admin-address in the list's keyring. We don't want to look up keys for subscriptions by email at all. (This was anyway only useful in the corner case where you prefilled a keyring to use for the new list.) * API: Access to `/status.json` is now allowed without authentication. * Deprecate X-LISTNAME in favour of X-LIST-NAME, for the sake of consistency in spelling keywords (but X-LISTNAME is still supported). (Thanks, maxigas!) ### Fixed * X-SUBSCRIBE now handles the combination of space-separated fingerprint and additional arguments (admin-flag, delivery-enabled-flag) correctly. * Fixed broken encoding of certain character-sequences in encrypted+signed messages. * X-LIST-KEYS again works without arguments. * X-RESEND now checks the given arguments to be valid email-addresses, and blocks resending if any one is found invalid. * X-RESEND now respects the encoding the mail was sent with. (Thanks, Lunar!) ## [3.1.2] / 2017-07-13 ### Changed * Sort lists alphabetically by email per default. ### Fixed * Fix dropping mails on certain headers (e.g. spam), as the headers weren't checked properly so far. * Fix processing of bounced messages. If a bounced messaged contained a PGP message (which most messages sent by schleuder have), schleuder tried to decrypt it before processing as a bounced message. This failed in nearly all cases, leading to double bounces. (#234) * Fix reading messages with empty Content-Type-header. Some automatically sent messages don't have one. * Do not try to fix text/plain messages from outlook (#246) ## [3.1.1] / 2017-06-24 ### Added * New cli-command `pin_keys` to pin the subscriptions of a list to a respective key (#225). Running this fixes the shortcoming of the code for list-migration mentioned below. ### Changed * Allow to run `refresh_keys` only for a given list. ### Fixed * **When migrating a v2-list, lookup keys for subscriptions** and assign the fingerprint if it was a distinct match. Otherwise people that had no fingerprint set before will receive plaintext emails — because in v3 we're not anymore looking up keys for subscriptions by email address. (To fix this for already migrated lists please use `schleuder pin_keys $listname`). * When migrating a v2-list, assign the looked up fingerprint to an admin only if it was a distinct match. * When migrating a v2-list, do not enable delivery for admins that weren't a member. (#213) * When migrating a v2-list, subscribe duplicated members only once (#208) * When migrating a v2-list, properly deal with admins that have no (valid) key. (#207) * When creating a list, only use distinctly found keys for admins. * Skip unusable keys when resending. * Don't report unchanged keys when refreshing keys. * Fix adding the subject-prefix to an empty subject (#226) * Do not detect emails sent from cron-scripts as bounces (#205) * Fix working with multipart/alternative-messages that contain inline OpenPGP-data. We're now stripping the HTML-part to enable properly handling the ciphertext. * Validate that an email address can be subscribed only once per list. * Fixed settings subscription-attributes (admin, delivery_enabled) when suscribing through schleuder-web. * schleuder-api-daemon SysV init script: Fix formatting and styling, add recommend and required commands {status,reload,force-reload} by Lintian. (#230) * Don't require database-adapter early. Helps when using a different database-system than sqlite. * Fix text of admin-notification from plugin-runners. * Avoid loops on notifying list admins (#229) ## [3.1.0] / 2017-05-21 ### Added * `X-GET-LOGFILE`. Sends you the logfile of the list. * `X-ATTACH-LISTKEY`. Attaches the list's key to a message. Useful in combination with `X-RESEND`. * `X-GET-VERSION`. Sends you the version of Schleuder that is running your list. * API-endpoint to trigger sending the list's key to all subscriptions. ### Changed * Don't write errors of list-plugins into the list of pseudo-headers. List-plugins must handle errors on their own. * Allow request-plugins to return attachments. * Fix x-get-key for multiple keys per match, and attach the resulting keys. * Tolerate 0x-prefix on input for fingerprints of subscriptions. * Tolerate spaces on input for fingerprints in keywords. * `X-GET-KEY` returns keys as attachments now. * `X-SIGN-THIS` returns attachments now, too. * The texts that describe the forwarded automated messages now reflect that not all of those were bounces. * Use single SQL-query instead of five, in select-statement in postfix/schleuder_sqlite.cf. * Use sender() to specify the return-address, instead of setting a Return-Path. ### Fixed * Make `public_footer` appear at the bottom of messages, not at the top. * Remove excessive empty lines in output of refresh-keys. * Amended list of dependencies in README. * Fix `X-GET-KEY` for multiple keys per match. * Also report if a key-import didn't change a present key. * Fix bounce-address in postfix/schleuder_sqlite.cf. ## [3.0.4] / 2017-04-15 ### Changed * Harmonize format of key `check` and `update` texts. ### Fixed * Fix unlegible messages (we mis-handled base64-encoded message-parts under some circumstances). * Avoid run-on paragraphs in key `check` and `update` reports (Thanks, dkg!) * Let schleuder-cli request check keys (allow /keys/check_keys.json to be reached). ## [3.0.3] / 2017-02-16 ### Changed * Require fingerprints of lists and subscriptions to be at least 32 characters long. Previously it was possible to assign shorter hexadecimal strings. * Key lookup for arguments to X-keywords is stricter than before: if you supply a string containing an "@", gnupg will be told to only match it against email-addresses; if you send a hexadecimal string, gnupg will be told to only match it against fingerprints. * Fixed and improved X-DELETE-KEY to only allow deletion of a single key, and only if no matching secret key is present. * Fixed and improved X-FETCH-KEY to use the configured keyserver; to handle URLs, fingerprints, and email-addresses alike; and to send internationalized messages. * Go back to make mock SKS-server listen on 127.0.0.1 — the former IP resulted in errors on some systems. ### Fixed * Don't break multipart/alternative-parts when inserting our pseudo-headers. * X-ADD-KEY handles inline content from Thunderbird correctly. * X-SIGN-THIS now looks recursively for attachments to sign. * Fixed unsubscribing oneself with X-UNSUBSCRIBE. * Fixed setting fingerprint for other subscription than oneself with X-SET-FINGERPRINT. * Better output of X-LIST-SUBSCRIPTIONS if no subscriptions are present. * Sensible error message if X-GET-KEY doesn't find a matching key. * Allow '0x'-prefix of fingerprints when given as keyword-argument. * If no keyword generates output, a sensible error message is used. ### Added * More rspec-tests. ## [3.0.2] / 2017-02-01 ### Changed * Use less usual IP and port number for mock SKS-server. Previously this conflicted with actual SKS-servers running on the same machine. ### Added * Call refresh_keys in provided crontab-script. ### Fixed * Fixed importing member-fingerprints when migrating a list. * Fix clearing passphrase during list-migration with GnuPG 2.0.x by actually shipping the required pinentry-script. * Corrected english phrasing and spelling for error message in case of wrong argument to listname-keyword. (Thanks, dkg!) ## [3.0.1] / 2017-01-26 ### Fixed * Fixed setting admin- and delivery-flags on subscription. Requests from schleuder-cli were interpreted wrongly, which led to new lists having no admins. * A short description for the man-page of schleuder-api-daemon to satisfy the lintian. * Listing openssl as dependency in README. If the openssl header-files are not present when eventmachine compiles its native code, schleuder-api-daemon cannot use TLS. * Removed reference to Github from Code of Conduct. ## [3.0.0] / 2017-01-26 ### Changed * **API-keys always required!** From now on all requests to schleuder-api-daemon require API-keys, even via localhost. This helps protecting against rogue non-root-accounts or -scripts on the local machine. * **TLS always used!** schleuder-api-daemon now always uses TLS. * Switched project-site and git-repository to . * Set proper usage flags when creating a new OpenPGP-key: the primary key gets "SC", the subkey "E". (Thanks, dkg!) * Avoid possible future errors by ignoring every unknown output of gpg (like GnuPG's doc/DETAILS recommends). (Thanks, dkg!) * Friendlier error message if delivery to subscription fails. * Set list-email as primary address after adding UIDs. Previously it was a little random, for reasons only known to GnuPG. * Only use temporary files where necessary, and with more secure paths. * Tighten requirements for valid email-addresses a little: The domain-part may now only contain alphanumeric characters, plus these: `._-` * Required version of schleuder-cli: 0.0.2. ### Added * X-LISTNAME: A **new mandatory keyword** to accompany all keywords. From now on every message containing keywords must also include the listname-keyword like this: `X-LISTNAME: list@hostname` The other keywords will only be run if the given listname matches the email-address of the list that the message is sent to. This mitigates replay-attacks among different lists. * Also send helpful message if a subscription's key is present but unusable. * Provide simpler postfix integration, now using virtual_domains and an sql-script. (Thanks, dkg!) * Enable refreshing keys from keyservers: A script that is meant to be run regularly from cron. It refreshes each key of each list one by one from a configurable keyserver, and sends the result to the respective list-admins. * Import attached, ascii-armored keys from messages with `add-key`-keyword. (Thanks, Kéfir!) * Check possible key-material for expected format before importing it. (Thanks, Kéfir!) ### Fixed * Allow fingerprints to be prefixed with '0x' in `subscribe`-keyword. * Also delete directory of list-logfile on deletion if that resides outside of the list-dir. * Sign and possibly encrypt error notifications. * Fix setting admin- and delivery-flags while subscribing. * Fix subscribing from schleuder-cli. * Fix finding subscriptions from signatures made by a signing-capable sub-key. ## [3.0.0.beta17] / 2017-01-12 ### Changed * Stopped using SCHLEUDER_ROOT in specs. Those make life difficult for packaging for debian. * While running specs, ensure smtp-daemon.rb has been stopped before starting it anew. ### Added * A Code of Conduct. ## [3.0.0.beta16] / 2017-01-11 ### Fixed * Fix running `schleuder migrate...`. * Fix assigning list-attributes when migrating a list. ### Added * Import the secret key and clear its passphrase when migrating a list from v2. * More tests. ## [3.0.0.beta15] / 2017-01-10 ### Changed * Default `lists_dir` and `listlogs_dir` to `/var/lib/schleuder`. * Use '/usr/local/bin' as daemon PATH in schleuder-api-daemon sysvinit script. ### Fixed * Fix running for fresh lists if `lists_dir` is different from `listlogs_dir` (by creating logfile-basedir, closes Debian bug #850545). * Fix error-message from ListBuilder if given email is invalid. * Fix checking for sufficient gpg-version (previously '2.1' didn't suffice if '2.1.0' was required). ### Added * Cron job file to check keys. * Show when delivery is disabled for a subscription (in reply to 'list-subscriptions'-keyword). * Add timeout to default sqlite-config (avoids errors in the case that the DB-file is locked on first attempt). * Provide method to call gpg-executable. * Also add additional UIDs to generated PGP-keys when using gpg 2.0. * Specs for ListBuilder. ## [3.0.0.beta14] / 2016-12-29 ### Fixed * Fix key expiry check * Fix link to schleuder.nadir.org in List-Help header * Fix deleting listdir ### Added * Runner and integration tests * More fixtures ## [3.0.0.beta13] / 2016-12-22 ### Fixed * Fix creating new lists. ## [3.0.0.beta12] / 2016-12-22 ### Changed * Show file permission warning if cert is being generated as root. * Use hard-coded defaults as base to merge config-file over. ### Added * New keyword `x-resend-cc` to send a message to multiple recipients that should know of each another. The ciphertext will be encrypted only once to all recipients, too. * More specs. * Skript for schleuder-api-daemon under sysvinit. ### Fixed * Fix tests for non-default listlogs_dir. * Fix pseudo-header "Sig" for unknown keys. * Fix adding subject_prefix_in for unencrypted messages. * Fix checking permissions of listdir and list.log for newly created lists. * Fix occasionally empty 'date'-pseudo-header. ## [3.0.0.beta11] / 2016-12-07 ### Changed * Fixed recognition and validation of clearsigned-inline messages. * Fix log-file rotation (for list.log). * Show hint to set `use_tls: true` after generation of certificate. ### Added * During installation, show error message and exit if data of an installation of schleuder-2.x is found in the configured lists_dir. * More tests. ## [3.0.0.beta10] / 2016-12-05 ### Changed * Fixed tarball to contain correct version and state of changelog. ## [3.0.0.beta9] / 2016-12-02 ### Added * Include tarball into release. * Make basedir of list-logs configurable (`listlogs_dir`). No operational change with the default value. * Recognize "encapsulated" signatures (RFC 3156, 6.1). (These signatures might still be reported as invalid, that's a bug in mail-gpg which will probably be fixed in their next release.) * Make installed schleuder-files accessible for owner and group only. * Make list-logs accessible to owner and group only. ### Changed * Improved documentation. ### Fixed * Fix checking for empty messages for nested multiparts (e.g. Thunderbird with memoryhole-headers). * Fix `schleuder install` to respect config settings (e.g. `lists_dir`) ## [3.0.0.beta8] / 2016-11-27 ### Changed * Add network and local-filesystem as dependencies in systemd-unit-file. * Improved documentation. ### Fixed * Declare dependency on thin. ## [3.0.0.beta7] / 2016-11-23 ### Added * `man`-page for schleuder(8). * schleuder-api-daemon: optionally use TLS. * schleuder-api-daemon: authenticate client by API-key if TLS is used. ### Changed * Sign git-tags, gems, and tarballs as 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3. * Rename schleuderd to schleuder-api-daemon. * schleuder-api-daemon: bind to `localhost` by default. * schleuder-api-daemon: changed name of `bind` config option to `host`. * schleuder-api-daemon: return 204 if not content is being sent along. * Refactor and improve model validations. ### Fixed * Fixed creating lists. * Fixed default config. * Log errors to syslog-logger in case of problems with list-dir. ## [3.0.0.beta6] / 2016-11-13 ### Added * Add `-v`, `--version` arguments to CLI. * New model validators. * Translations (de, en) and better wording for validation error messages. * Specs (test-cases) for the list model. * Use Travis to automate testing. * Test listname to be a valid email address before creating list. * A simple contribution guide. * Check that GnuPG >= 2.0 is being used. * Enable to specify path to gpg-executable in GPGBIN environment variable. * A simple schleuder-only MTA to help with development. ### Changed * schleuderd: use GET instead of OPTIONS to work around bug in ruby 2.1. * Allow "inline"-pgp for request-messages (mail-gpg 0.2.7 fixed their issue). ### Fixed * Fix testing nested messages for emptiness. * Fix bouncing a message if it was found to be empty. * Fix truncated 'adding UID failed' message (transported via HTTP-headers). ## ... --------- The format of this file is based on [Keep a Changelog](http://keepachangelog.com/). Template, please ignore: ## [x.x.x] / YYYY-MM-DD ### Added ### Changed ### Deprecated ### Removed ### Fixed ### Security schleuder-5.0.1/CODE_OF_CONDUCT.md000066400000000000000000000044031502127241700162740ustar00rootroot00000000000000# Schleuder's Code of Conduct The Schleuder development team wants to provide an open and welcoming environment for everyone who participates in the development of Schleuder regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, physical appearance, race, ethnicity, economic or social status, age, religion, or nationality. Harassment in code and discussion or violation of physical boundaries is completely unacceptable anywhere in the Schleuder project. We welcome contributions from everyone that adheres to these principles. Violators will be warned, blocked or banned by the development team. ### In detail Harassment includes offensive verbal comments related to level of experience, gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, ethnicity, age, religion, nationality, the use of sexualized language or imagery, deliberate intimidation, stalking, sustained disruption, and unwelcome sexual attention. Individuals asked to stop any harassing behavior are expected to comply immediately. Maintainers, including the development team, are also subject to the anti-harassment policy. If anyone engages in abusive, harassing, or otherwise unacceptable behavior, including maintainers, we will take appropriate action, up to and including warning the offender, deletion of comments, removal from the project’s codebase and communication systems. If you are being harassed, notice that someone else is being harassed, or have any other concerns, please [contact the development team](https://schleuder.org/contact.html) immediately. We expect everyone to follow these rules anywhere in the Schleuder project’s codebases, issue trackers, IRC channel, group chat, mailing lists, meetups, and any other events. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Finally, don't forget that it is human to make mistakes! We all do. Let’s work together to help each other, resolve issues, and learn from the mistakes that we will all inevitably make from time to time. ### Thanks Thanks to the [Bundler Code of Conduct](https://bundler.io/conduct.html), which we adopted for ourselves. schleuder-5.0.1/CONTRIBUTING.md000066400000000000000000000076761502127241700157450ustar00rootroot00000000000000Contributing ============ To contribute please follow this workflow: 1. Talk to us! E.g. create an issue about your idea or problem. 2. Fork the repository and work in a meaningful named branch that is based off of our "main". 3. Enable CI: In your fork, go to Settings -> CI / CD -> Expand next to Runners settings -> Enable shared Runners. 4. Commit in rather small chunks but don't split depending code across commits. Please write sensible commit messages. 5. Please add tests for your feature or bugfix. 6. If in doubt request feedback from us! 7. When finished create a merge request. Thank you for your interest! ## Tutorial: Getting started with schleuder development This short beginner's guide helps you getting started in schleuder development. ### Set up environment First, we have to set up the development environment. We use [rvm](https://rvm.io/rvm/basics) to avoid dependency version mixups with other ruby projects. 1. Install ruby and [rvm](https://rvm.io/rvm/basics). 2. Clone the schleuder repository and cd into it. 3. Create a rvm gemset: ``` rvm gemset create schleuder rvm gemset use schleuder rvm gemset list # checks if it uses the correct gemset ``` 4. Install bundler: `gem install bundler` 5. Install dependencies with bundler: ``` bundle install ``` ### Getting started with development To start with the actual development, we first have to get the schleuder system running. 1. Install schleuder: `rvmsudo bin/schleuder install` 2. Set permissions: ``` sudo chown -R /var/lib/schleuder/ sudo chown -R /etc/schleuder/ ``` 3. Clone and install schleuder-cli: ``` wget https://0xacab.org/schleuder/schleuder-cli/raw/main/gems/schleuder-cli-0.1.0.gem.sig && gpg --verify schleuder-cli-0.1.0.gem.sig gem install schleuder-cli-0.1.0.gem ``` 4. Configure schleuder-cli: ``` schleuder cert fingerprint | cut -d ' ' -f 4 # copy the output to tls_fingerprint in ~/.schleuder-cli/schleuder-cli.yml schleuder new_api_key # copy the output to api_key in ~/.schleuder-cli/schleuder-cli.yml AND /etc/schleuder/schleuder.yml ``` 5. Start schleuder-api-daemon: `schleuder-api-daemon` 6. Test if everything works by running (should not return an error): `schleuder-cli lists list` #### Manual testing and debugging After we have implemented our code, it is time to test and debug it. To do that manually, we create a test list via schleuder-cli and throw in test emails. There is a mock smtp daemon in `bin/schleuder-smtpd.rb` to help us test without a fully configured mail server. This short tutorial will help you initialize the manual debug setup. 0. Create a list with a test public key: ``` gpg --export --armor > pubkey.gpg schleuder-cli lists new test@test pubkey.gpg ``` 1. Start fake smtp daemon: `rvmsudo ruby bin/schleuder-smtpd.rb`. Throw a test mail into it to verify it is running: ``` echo "test" | mailx -s "test" -S smtp=smtp://localhost list@list ``` 2. Create an email to send and store it in a file (e.g. `mail.txt`): ``` To: test@test From: root@localhost Subject: Test Test ``` 3. Throw email in `cat mail.txt | schleuder work test@test`. The receivers of the mail can be seen in the output of `schleuder-smtpd.rb` 4. Start debugging your code! From that we additionally can use schleuder-cli to set the list options or create different test scenarios. Remember to restart the `schleuder-api-daemon` if you make any changes to the list config options. #### Changing the database - To change the database add a file to `db/migrate/` that has the same structure as the files in there. Then call `rake db:migrate`. - To downgrade a specific change use: `rake db:migrate:down VERSION=` - To upgrade a specific change use: `rake db:migrate:up VERSION=` #### Automated tests - Automated tests are defined in `spec/` - Execute automated tests: ``` SCHLEUDER_ENV=test SCHLEUDER_CONFIG=spec/schleuder.yml bundle exec rake db:init bundle exec rspec ``` schleuder-5.0.1/Gemfile000066400000000000000000000001371502127241700147700ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in schleuder.gemspec gemspec schleuder-5.0.1/LICENSE.txt000066400000000000000000001045131502127241700153230ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . schleuder-5.0.1/MISSION_STATEMENT.md000066400000000000000000000014341502127241700165650ustar00rootroot00000000000000# Schleuder's Mission Statement We give our time and knowledge to build and maintain this project in order to help people with their daily private communication and in the struggle for their personal emancipation, social and economic justice and political freedom. A more human world, in which anyone can be different without fear, is what we strive for. We work transparently and responsibly, and don't collaborate with surveillance actors. We do not endorse any form of authoritarian movement or totalitarian system, and wish for that Schleuder may not ever be used to promote or enable them. Instead we are committed to ideas and principles outlined in our [Code of Conduct](https://0xacab.org/schleuder/schleuder/blob/main/CODE_OF_CONDUCT.md). If you do not agree, do not use Schleuder. schleuder-5.0.1/README.md000066400000000000000000000111161502127241700147530ustar00rootroot00000000000000Schleuder ====================================== Schleuder is a gpg-enabled mailing list manager with resending-capabilities. Subscribers can communicate encrypted (and pseudonymously) among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list. It aims to be robust, flexible, internationalized and also provides an API for the optional web interface called [schleuder-web](https://0xacab.org/schleuder/schleuder-web). For more details see . Maintainers wanted! ------------------- This project needs additional maintainers. All of us in the team have hardly any time for the project anymore. We don't want Schleuder to die, and we're not dropping it right now. But for a sustainable future, Schleuder needs new humans to care for it. For details please see . Requirements ------------ * ruby >=2.7 * gnupg >=2.2 * gpgme * sqlite3 * openssl * icu * libcurl *If you use Debian buster, CentOS 7 or Archlinux, please have a look at the [installation docs](https://schleuder.org/schleuder/docs/server-admins.html#installation). We do provide packages for those platforms, which simplify the installation a lot.* We **recommend** to also run a random number generator like [haveged](http://www.issihosts.com/haveged/). This ensures Schleuder won't be blocked by lacking entropy, which otherwise might happen especially during key generation. Additionally these **rubygems** are required (will be installed automatically unless present): * rake * active_record * sqlite3 * thor * thin * mail-gpg * sinatra * sinatra-contrib Installing Schleuder ------------ 1. Download [the gem](https://schleuder.org/download/schleuder-5.0.1.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-5.0.1.gem.sig) and verify: ``` gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 gpg --verify schleuder-5.0.1.gem.sig ``` 2. Install required packages to facilitate installation of the gem (command tested on Deban version 12 - codename bookworm) ``` apt install autoconf g++ gcc libsqlite3-dev libssl-dev libxml2-dev libz-dev make ruby-bundler ruby-dev ruby-rubygems ``` 3. If all went well install the gem: ``` gem install schleuder-5.0.1.gem ``` 4. Set up schleuder: ``` schleuder install ``` This creates necessary directories, copies example configs, etc. If you see errors about missing write permissions please follow the advice given. For further information on setup and configuration please read . Command line usage ----------------- See `schleuder help`. E.g.: Commands: schleuder check_keys # Check all lists for unusable or expiring keys and send the results to the list-admins. (This is supposed... schleuder help [COMMAND] # Describe available commands or one specific command schleuder install # Set up Schleuder initially. Create folders, copy files, fill the database, etc. schleuder version # Show version of schleuder schleuder work list@hostname < message # Run a message through a list. List administration ------------------- Please use [schleuder-cli](https://0xacab.org/schleuder/schleuder-cli) to create and manage lists from the command line. Optionally consider installing [schleuder-web](https://0xacab.org/schleuder/schleuder-web), the web interface for schleuder. It enables list-admins to manage their lists through the web instead of using [request-keywords](https://schleuder.org/docs/#subscription-and-key-management). Todo ---- See . Testing ------- We use rspec to test our code. To setup the test environment run: SCHLEUDER_ENV=test SCHLEUDER_CONFIG=spec/schleuder.yml bundle exec rake db:init To execute the test suite run: bundle exec rspec We are working on extendig the test coverage. Contributing ------------ Please see [CONTRIBUTING.md](CONTRIBUTING.md). Mission statement ----------------- Please see [MISSION_STATEMENT.md](MISSION_STATEMENT.md). Code of Conduct --------------- We adopted a code of conduct. Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). License ------- GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt). Alternative Download -------------------- Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-5.0.1.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-5.0.1.tar.gz.sig). schleuder-5.0.1/Rakefile000066400000000000000000000071231502127241700151440ustar00rootroot00000000000000project = 'schleuder' require_relative "lib/#{project}.rb" @version = Schleuder::VERSION @tagname = "#{project}-#{@version}" @gpguid = 'B3D190D5235C74E1907EACFE898F2C91E2E6E1F3' @filename_gem = "#{@tagname}.gem" @filename_tarball = "#{@tagname}.tar.gz" # Make ActiveRecord's tasks usable for us (without this, those tasks try to run # in the "default_env" environment, which is not configured). ENV['RACK_ENV'] ||= ENV['SCHLEUDER_ENV'] load 'active_record/railties/databases.rake' # Configure ActiveRecord ActiveRecord::Tasks::DatabaseTasks.tap do |config| config.root = File.dirname(__FILE__) config.db_dir = 'db' config.migrations_paths = ['db/migrate'] config.env = ENV['SCHLEUDER_ENV'] config.database_configuration = Schleuder::Conf.databases end # ActiveRecord requires this task to be present Rake::Task.define_task('db:environment') namespace :db do # A shortcut. task init: ['db:create', 'db:schema:load'] end def edit_and_add_file(filename) puts "Please edit #{filename} to refer to version #{@version}" if system("gvim -f #{filename}.md") `git add #{filename}.md` else exit 1 end end task :console do exec "irb -r #{File.dirname(__FILE__)}/lib/schleuder.rb" end task :publish_gem => :website task :git_tag => :check_version desc 'Build new version: git-tag and gem-file' task :new_version => [ :check_version, :edit_readme, :edit_changelog, :git_add_version, :git_commit, :build_gem, :sign_gem, :build_tarball, :sign_tarball, :ensure_permissions, :git_tag ] do end desc 'Edit CHANGELOG.md' task :edit_changelog do edit_and_add_file('CHANGELOG') end desc 'Edit README' task :edit_readme do edit_and_add_file('README') end desc 'git-tag HEAD as new version' task :git_tag do `git tag -u #{@gpguid} -s -m "Version #{@version}" #{@tagname}` end desc 'Add changed version to git-index' task :git_add_version do `git add lib/#{project}/version.rb` end desc 'Commit changes as new version' task :git_commit do `git commit -m "Version #{@version}"` end desc 'Build, sign and commit a gem-file.' task :build_gem do `gem build #{project}.gemspec` end desc 'OpenPGP-sign gem and tarball' task :sign_tarball do `gpg -u #{@gpguid} -b #{@filename_tarball}` end desc 'OpenPGP-sign gem' task :sign_gem do `gpg -u #{@gpguid} -b #{@filename_gem}` end desc 'Ensure download-files have correct permissions' task :ensure_permissions do File.chmod(0644, *Dir.glob("#{@tagname}*")) end desc 'Upload download-files (gem, tarball, signatures) to schleuder.org.' task :upload_files do puts `echo "put -p #{@tagname}* www/download/" | sftp schleuder.org@ftp.schleuder.org 2>&1` end desc 'Publish gem-file to rubygems.org' task :publish_gem do puts "Really push #{@filename_gem} to rubygems.org? [yN]" if $stdin.gets.match(/^y/i) puts 'Pushing...' `gem push #{@filename_gem}` else puts 'Not pushed.' end end desc 'Build and sign a tarball' task :build_tarball do `git archive --format tar.gz --prefix "#{@tagname}/" -o #{@filename_tarball} main` end desc 'Describe manual release-tasks' task :website do puts 'Please remember to publish the release-notes on the website and on schleuder-announce.' end desc 'Check if version-tag already exists' task :check_version do # Check if Schleuder::VERSION has been updated since last release if `git tag`.match?(/^#{@tagname}$/) $stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update lib/#{project}/version.rb?" $stderr.print 'Delete tag to continue? [yN] ' if $stdin.gets.match(/^y/i) `git tag -d #{@tagname}` else exit 1 end end end schleuder-5.0.1/bin/000077500000000000000000000000001502127241700142445ustar00rootroot00000000000000schleuder-5.0.1/bin/schleuder000077500000000000000000000005531502127241700161530ustar00rootroot00000000000000#!/usr/bin/env ruby # Don't emit any warnings, they would be sent back to the email-sender as an # error-message. $VERBOSE=nil trap('INT') { exit 1 } begin $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) require 'schleuder/cli' Schleuder::Cli.start rescue => exc $stderr.puts "Technical Error: #{exc}\n#{exc.backtrace.first}" exit 1 end schleuder-5.0.1/bin/schleuder-api-daemon000077500000000000000000000002101502127241700201510ustar00rootroot00000000000000#!/usr/bin/env ruby $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) require 'schleuder-api-daemon' SchleuderApiDaemon.run! schleuder-5.0.1/bin/schleuder-bundler000077500000000000000000000003631502127241700176030ustar00rootroot00000000000000#!/bin/bash -l export HOME=/home/$(id -nu) # Little helper script to call schleuder through bundler. Useful e.g. for # hooking up development code into postfix. bindir="$(realpath $(dirname $0))" cd $bindir/.. bundle exec ./bin/schleuder $@ schleuder-5.0.1/bin/schleuder-smtpd.rb000077500000000000000000000032761502127241700177070ustar00rootroot00000000000000#!/usr/bin/env ruby # # This script is a very simple SMTP-daemon, that delivers every incoming email # to a given schleuder-list. It's meant to help developing without a real, # local MTA. require 'socket' require 'open3' trap ('INT') { exit 0 } def usage puts "Usage: #{File.basename(__FILE__)} [-p portnum]" exit 1 end # get args case ARGV.first when '-h', '--help', 'help' usage when '-p' port = ARGV[1].to_i if port == 0 usage end end port ||= 25 schleuderbin = File.join(File.dirname(__FILE__), 'schleuder') begin # run the server server = TCPServer.new('127.0.0.1', port) # receive input while (connection = server.accept) input = '' recipient = '' connection.puts '220 localhost SMTP' begin while line = connection.gets line.chomp! case line[0..3].downcase when 'ehlo', 'helo' connection.puts '250 localhost' when 'mail', 'rset' connection.puts '250 ok' when 'rcpt' recipient = line.split(':').last.gsub(/[<>\s]*/, '') connection.puts '250 ok' when 'data' connection.puts '354 go ahead' when 'quit' connection.puts '221 localhost' when '.' puts "New message to #{recipient}" err, status = Open3.capture2e("#{schleuderbin} work #{recipient}", {stdin_data: input}) if status.exitstatus > 0 puts "Error from schleuder: #{err}." connection.puts "550 #{err}" else connection.puts '250 ok' end else input << line + "\n" end end rescue IOError end connection.close end rescue => exc $stderr.puts exc exit 1 end schleuder-5.0.1/db/000077500000000000000000000000001502127241700140615ustar00rootroot00000000000000schleuder-5.0.1/db/migrate/000077500000000000000000000000001502127241700155115ustar00rootroot00000000000000schleuder-5.0.1/db/migrate/20140501103532_create_lists.rb000066400000000000000000000031751502127241700221570ustar00rootroot00000000000000class CreateLists < ActiveRecord::Migration[4.2] def up if ! table_exists?(:lists) create_table :lists do |t| t.timestamps t.string :email t.string :fingerprint t.string :gpg_passphrase t.string :log_level, default: 'warn' t.string :default_mime, default: 'mime' t.string :subject_prefix, default: '' t.string :subject_prefix_in, default: '' t.string :subject_prefix_out, default: '' t.string :openpgp_header_preference, default: 'signencrypt' t.text :public_footer, default: '' t.text :headers_to_meta, default: '["from","to","date",":cc"]' t.text :bounces_drop_on_headers, default: '{"x-spam-flag":"yes"}' t.text :keywords_admin_only, default: '["unsubscribe", "unsubscribe", "delete-key"]' t.text :keywords_admin_notify, default: '["add-key"]' t.boolean :send_encrypted_only, default: false t.boolean :receive_encrypted_only, default: false t.boolean :receive_signed_only, default: false t.boolean :receive_authenticated_only, default: false t.boolean :receive_from_subscribed_emailaddresses_only, default: false t.boolean :receive_admin_only, default: false t.boolean :keep_msgid, default: true t.boolean :bounces_drop_all, default: false t.boolean :bounces_notify_admins, default: true t.boolean :include_list_headers, default: true t.boolean :include_openpgp_header, default: true t.integer :max_message_size_kb, default: 10240 end end def down drop_table(:lists) end end end schleuder-5.0.1/db/migrate/20140501112859_create_subscriptions.rb000066400000000000000000000010201502127241700237270ustar00rootroot00000000000000class CreateSubscriptions < ActiveRecord::Migration[4.2] def up if ! table_exists?(:subscriptions) create_table :subscriptions do |t| t.integer :list_id t.string :email t.string :fingerprint t.boolean :admin, default: false t.boolean :delivery_disabled, default: false t.timestamps end add_index :subscriptions, :list_id add_index :subscriptions, [:email, :list_id], unique: true end end def down drop_table(:subscriptions) end end schleuder-5.0.1/db/migrate/20150809210000_add_language_to_lists.rb000066400000000000000000000003601502127241700240030ustar00rootroot00000000000000class AddLanguageToLists < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :language) add_column :lists, :language, :string, default: 'en' end end def down remove_column(:lists, :language) end end schleuder-5.0.1/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb000066400000000000000000000005011502127241700267530ustar00rootroot00000000000000class ChangeKeywordsAdminOnlyDefaults < ActiveRecord::Migration[4.2] def up change_column_default :lists, :keywords_admin_only, "[\"subscribe\", \"unsubscribe\", \"delete-key\"]" end def down change_column_default :lists, :keywords_admin_only, "[\"unsubscribe\", \"unsubscribe\", \"delete-key\"]" end end schleuder-5.0.1/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb000066400000000000000000000004771502127241700265370ustar00rootroot00000000000000class AddForwardAllIncomingToAdmins < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :forward_all_incoming_to_admins) add_column :lists, :forward_all_incoming_to_admins, :boolean, default: false end end def down remove_column(:lists, :forward_all_incoming_to_admins) end end schleuder-5.0.1/db/migrate/20150814172700_change_send_encrypted_only_default.rb000066400000000000000000000003431502127241700265630ustar00rootroot00000000000000class ChangeSendEncryptedOnlyDefault < ActiveRecord::Migration[4.2] def up change_column_default :lists, :send_encrypted_only, true end def down change_column_default :lists, :send_encrypted_only, false end end schleuder-5.0.1/db/migrate/20150822214300_add_logfiles_to_keep_to_lists.rb000066400000000000000000000004141502127241700255340ustar00rootroot00000000000000class AddLogfilesToKeepToLists < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :logfiles_to_keep) add_column :lists, :logfiles_to_keep, :integer, default: 2 end end def down remove_column(:lists, :logfiles_to_keep) end end 20150826172300_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb000066400000000000000000000007371502127241700343450ustar00rootroot00000000000000schleuder-5.0.1/db/migrateclass RenameDeliveryDisabledToDeliveryEnabledAndChangeDefault < ActiveRecord::Migration[4.2] def up if column_exists?(:subscriptions, :delivery_disabled) rename_column :subscriptions, :delivery_disabled, :delivery_enabled change_column_default :subscriptions, :delivery_enabled, true end end def down rename_column :subscriptions, :delivery_enabled, :delivery_disabled change_column_default :subscriptions, :delivery_disabled, false end end schleuder-5.0.1/db/migrate/20150826181500_strip_gpg_passphrase.rb000066400000000000000000000003601502127241700237320ustar00rootroot00000000000000class StripGpgPassphrase < ActiveRecord::Migration[4.2] def up if column_exists?(:lists, :gpg_passphrase) remove_column :lists, :gpg_passphrase end end def down add_column :lists, :gpg_passphrase, :string end end schleuder-5.0.1/db/migrate/20150826182700_remove_default_mime.rb000066400000000000000000000003031502127241700235130ustar00rootroot00000000000000class RemoveDefaultMime < ActiveRecord::Migration[4.2] def up remove_column :lists, :default_mime end def down add_column :lists, :default_mime, :string, default: 'mime' end end schleuder-5.0.1/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb000066400000000000000000000002661502127241700253610ustar00rootroot00000000000000class FixHeadersToMetaDefaults < ActiveRecord::Migration[4.2] def up change_column_default :lists, :headers_to_meta, '["from", "to", "date", "cc"]' end def down end end schleuder-5.0.1/db/migrate/20170713215059_add_internal_footer_to_list.rb000066400000000000000000000004061502127241700252470ustar00rootroot00000000000000class AddInternalFooterToList < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :internal_footer) add_column :lists, :internal_footer, :text, default: '' end end def down remove_column(:lists, :internal_footer) end end schleuder-5.0.1/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb000066400000000000000000000017511502127241700274770ustar00rootroot00000000000000class AddSigEncToHeadersToMetaDefaults < ActiveRecord::Migration[4.2] def up change_column_default :lists, :headers_to_meta, '["from", "to", "cc", "date", "sig", "enc"]' list_klass = create_list_klass list_klass.reset_column_information list_klass.find_each do |list| if (list.headers_to_meta & ['sig', 'enc']).empty? list.update(headers_to_meta: list.headers_to_meta + ['sig', 'enc']) end end end def down change_column_default :lists, :headers_to_meta, '["from", "to", "cc", "date"]' list_klass = create_list_klass list_klass.reset_column_information list_klass.find_each do |list| list.update(headers_to_meta: list.headers_to_meta - ['enc','sig']) end end def create_list_klass # Use a temporary class-definition to be independent of the # complexities of the actual class. Class.new(ActiveRecord::Base) do self.table_name = 'lists' self.serialize :headers_to_meta, coder: JSON end end end schleuder-5.0.1/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb000066400000000000000000000004171502127241700254140ustar00rootroot00000000000000class AddDeliverSelfsentToList < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :deliver_selfsent) add_column :lists, :deliver_selfsent, :boolean, default: true end end def down remove_column(:lists, :deliver_selfsent) end end schleuder-5.0.1/db/migrate/20190906194820_add_autocrypt_header_to_list.rb000066400000000000000000000004471502127241700254340ustar00rootroot00000000000000class AddAutocryptHeaderToList < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :include_autocrypt_header) add_column :lists, :include_autocrypt_header, :boolean, default: true end end def down remove_column(:lists, :include_autocrypt_header) end end schleuder-5.0.1/db/migrate/20200118170110_add_set_reply_to_to_sender_and_munge_from.rb000066400000000000000000000007061502127241700301120ustar00rootroot00000000000000class AddSetReplyToToSenderAndMungeFrom < ActiveRecord::Migration[4.2] def up if ! column_exists?(:lists, :set_reply_to_to_sender) add_column :lists, :set_reply_to_to_sender, :boolean, default: false end if ! column_exists?(:lists, :munge_from) add_column :lists, :munge_from, :boolean, default: false end end def down remove_column(:lists, :set_reply_to_to_sender) remove_column(:lists, :munge_from) end end schleuder-5.0.1/db/migrate/20211106112020_change_boolean_values_to_integers.rb000066400000000000000000000035131502127241700263670ustar00rootroot00000000000000# Since ActiveRecord >= 6.0, the SQLite3 connection adapter relies on boolean # serialization to use 1 and 0, but does not natively recognize 't' and 'f' as # booleans were previously serialized. # # Accordingly, this migration handles conversion of both column defaults and # stored data provided by a user. # # In contrast to other migrations, only a 'forward' method is provided, a # mechanism to 'reverse' is not. Given the nature of this migration, the later # is not really required. # # Unfortunately, we missed this breaking change when bumping ActiveRecord to >= # 6.0 in Schleuder version 4.0. This caused quite some work upstream, but also # in downstream environments and, last but not least, at the side of users. # # We should extend our CI to explicitly test, and ensure things work as # expected, if running a Schleuder setup in real world. As of now, we don't # ensure data provided by a user in Schleuder version x still works after # upgrading to version y. class ChangeBooleanValuesToIntegers < ActiveRecord::Migration[6.0] class Lists < ActiveRecord::Base end class Subscriptions < ActiveRecord::Base end def up [Lists, Subscriptions].each do |table| unless table.connection.is_a?(ActiveRecord::ConnectionAdapters::SQLite3Adapter) return end bool_columns_defaults = table.columns.select { |column| column.type == :boolean }.map{ |column| [column.name, column.default] } bool_columns_defaults.each do |column_name, column_default| column_bool = ActiveRecord::Type::Boolean.new.deserialize(column_default) change_column_default :"#{table.table_name}", :"#{column_name}", column_bool table.where("#{column_name} = 'f'").update_all("#{column_name}": 0) table.where("#{column_name} = 't'").update_all("#{column_name}": 1) end end end end schleuder-5.0.1/db/migrate/20211107151309_add_limits_to_string_columns.rb000066400000000000000000000016551502127241700254460ustar00rootroot00000000000000# It seems, the change to ActiveRecord >= 6.0 makes this necessary. # Without doing this, the auto-generated database schema file would drop # these limits, if running migrations. # # In contrast to other migrations, only a 'forward' method is provided, a # mechanism to 'reverse' is not. Given the nature of this migration, the later # is not really required. # # This has been an upstream issue for quite some time. For details, see # https://github.com/rails/rails/issues/19001. class AddLimitsToStringColumns < ActiveRecord::Migration[6.0] class Lists < ActiveRecord::Base end class Subscriptions < ActiveRecord::Base end def up [Lists, Subscriptions].each do |table| string_columns = table.columns.select { |column| column.type == :string }.map(&:name) string_columns.each do |column_name| change_column :"#{table.table_name}", :"#{column_name}", :string, :limit => 255 end end end end schleuder-5.0.1/db/migrate/20220910170110_add_key_auto_import_from_email.rb000066400000000000000000000004571502127241700257110ustar00rootroot00000000000000class AddKeyAutoImportFromEmail < ActiveRecord::Migration[7.1] def up if ! column_exists?(:lists, :key_auto_import_from_email) add_column :lists, :key_auto_import_from_email, :boolean, default: false end end def down remove_column(:lists, :key_auto_import_from_email) end end schleuder-5.0.1/db/schema.rb000066400000000000000000000062761502127241700156610ustar00rootroot00000000000000# This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # # This file is the source Rails uses to define your schema when running `bin/rails # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema[7.1].define(version: 2022_09_10_170110) do create_table "lists", force: :cascade do |t| t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.string "email", limit: 255 t.string "fingerprint", limit: 255 t.string "log_level", limit: 255, default: "warn" t.string "subject_prefix", limit: 255, default: "" t.string "subject_prefix_in", limit: 255, default: "" t.string "subject_prefix_out", limit: 255, default: "" t.string "openpgp_header_preference", limit: 255, default: "signencrypt" t.text "public_footer", default: "" t.text "headers_to_meta", default: "[\"from\", \"to\", \"cc\", \"date\", \"sig\", \"enc\"]" t.text "bounces_drop_on_headers", default: "{\"x-spam-flag\":\"yes\"}" t.text "keywords_admin_only", default: "[\"subscribe\", \"unsubscribe\", \"delete-key\"]" t.text "keywords_admin_notify", default: "[\"add-key\"]" t.boolean "send_encrypted_only", default: true t.boolean "receive_encrypted_only", default: false t.boolean "receive_signed_only", default: false t.boolean "receive_authenticated_only", default: false t.boolean "receive_from_subscribed_emailaddresses_only", default: false t.boolean "receive_admin_only", default: false t.boolean "keep_msgid", default: true t.boolean "bounces_drop_all", default: false t.boolean "bounces_notify_admins", default: true t.boolean "include_list_headers", default: true t.boolean "include_openpgp_header", default: true t.integer "max_message_size_kb", default: 10240 t.string "language", limit: 255, default: "en" t.boolean "forward_all_incoming_to_admins", default: false t.integer "logfiles_to_keep", default: 2 t.text "internal_footer", default: "" t.boolean "deliver_selfsent", default: true t.boolean "include_autocrypt_header", default: true t.boolean "set_reply_to_to_sender", default: false t.boolean "munge_from", default: false t.boolean "key_auto_import_from_email", default: false end create_table "subscriptions", force: :cascade do |t| t.integer "list_id" t.string "email", limit: 255 t.string "fingerprint", limit: 255 t.boolean "admin", default: false t.boolean "delivery_enabled", default: true t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil t.index ["email", "list_id"], name: "index_subscriptions_on_email_and_list_id", unique: true t.index ["list_id"], name: "index_subscriptions_on_list_id" end end schleuder-5.0.1/etc/000077500000000000000000000000001502127241700142475ustar00rootroot00000000000000schleuder-5.0.1/etc/init.d/000077500000000000000000000000001502127241700154345ustar00rootroot00000000000000schleuder-5.0.1/etc/init.d/schleuder-api-daemon000077500000000000000000000042721502127241700213550ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: schleuder-api-daemon # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Schleuder API daemon # Description: Schleuder API daemon — provides access for schleuder-cli and schleuder-web ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin . /lib/init/vars.sh . /lib/lsb/init-functions NAME=schleuder-api-daemon DAEMON=/usr/local/bin/schleuder-api-daemon DESC="Schleuder API daemon" PIDFILE=/var/run/$NAME.pid USER=schleuder GROUP=schleuder test -x $DAEMON || exit 5 start_schleuder_api_daemon() { if [ -f "$PIDFILE" ]; then echo "" log_failure_msg "$DESC is running already, please stop it first" exit 1 fi if ! id $USER >/dev/null 2>&1; then log_failure_msg "User \"$USER\" does not exist" exit 1 fi if ! getent group $GROUP >/dev/null 2>&1; then log_failure_msg "Group \"$GROUP\" does not exist" exit 1 fi start-stop-daemon --chuid "$USER":"$GROUP" --start --pidfile $PIDFILE --make-pidfile --background --startas $DAEMON -- $NAME } stop_schleuder_api_daemon() { if [ -f "$PIDFILE" ]; then start-stop-daemon --stop --retry TERM/10/KILL/5 --pidfile $PIDFILE --quiet --oknodo --pidfile "$PIDFILE" rm -f $PIDFILE fi } status_schleuder_api_daemon() { if [ ! -e $PIDFILE ]; then status_of_proc "${DAEMON}" "${DESC}" else status_of_proc -p "${PIDFILE}" "${DAEMON}" "${DESC}" fi } case "$1" in start) log_begin_msg "Starting $DESC" start_schleuder_api_daemon log_end_msg $? ;; stop) log_begin_msg "Stopping $DESC" stop_schleuder_api_daemon log_end_msg $? ;; status) status_schleuder_api_daemon ;; restart|reload|force-reload) log_begin_msg "Restarting $DESC" stop_schleuder_api_daemon start_schleuder_api_daemon log_end_msg $? ;; *) echo "Usage: $0 {start|stop|status|restart|reload|force-reload}" exit 1 ;; esac schleuder-5.0.1/etc/list-defaults.yml000066400000000000000000000144321502127241700175560ustar00rootroot00000000000000# Setting default values for newly generated lists. Once a list is created it # is not affected by these settings but has its own set of options in the # database. # # The configuration format is yaml (http://www.yaml.org). # # Options are listed with the behaviour encoded in the database schema. # Only send out encrypted emails to subscriptions? # (This setting does not affect resend-messages.) send_encrypted_only: true # Allow only encrypted emails? If true, any other email will be bounced. receive_encrypted_only: false # Allow only emails that are validly signed? If true, any other email will be # bounced. receive_signed_only: false # Allow only emails that are validly signed by a subscriber's key? If true, # any other email will be bounced. receive_authenticated_only: false # Allow only emails being sent from subscribed addresses? If true, any other # email will be bounced. # NOTE: This is a very weak restriction mechanism on which you should not rely, # as sending addresses can easily be faked! We recommend you to rather # rely on the `receive_authenticated_only` option. Setting the # `receive_authenticated_only` option to true, will authenticate senders # based on the signature on the mail, which is the strongest # authentication mechanism you can get. # This option could be useful, if you would like to have a closed # mailinglist, but could not yet get all subscribers to properly use GPG. receive_from_subscribed_emailaddresses_only: false # Allow only emails that are validly signed by a list-admin's key. # This is useful for newsletters, announce or notification lists receive_admin_only: false # Which headers to include from the original mail. headers_to_meta: - from - to - cc - date - sig - enc # Preserve the Message-IDs (In-Reply-To, References) from the incoming email. # This setting can lead to information leakage, as replies are connectable # and a thread of (encrypted) messages can be built by an eavesdropper. keep_msgid: true # Which keywords ("email-commands") should be restricted to list-admins? keywords_admin_only: - subscribe - unsubscribe - delete-key # For which keywords should the list-admins receive a notice whenever it # triggers a command. keywords_admin_notify: - add-key # Footer to append to each email that is sent to a subscribed address. Will not # be included in messages to non-subscribed addresses. internal_footer: # Footer to append to each email that is sent to non-subscribed addresses. Will # not be included in messages to subscribed addresses. public_footer: # Prefix to be inserted into the subject of every email that is validly signed # by a subscribed address. subject_prefix: # Prefix to be inserted into the subject of every email that is *not* validly # signed by a subscribed address. subject_prefix_in: # Prefix to be inserted into the subject of every email that has been # resent to a non-subscribed address. subject_prefix_out: # Drop any bounces (incoming emails not passing the receive_*_only-rules)? bounces_drop_all: false # Drop bounces if they match one of these headers. Must be a hash, keys # and values are case insensitive. bounces_drop_on_headers: x-spam-flag: yes # Send a notice to the list-admins whenever an email is bounced or dropped? bounces_notify_admins: true # Include Autocrypt header into emails? include_autocrypt_header: true # Include RFC-compliant List-* Headers into emails? include_list_headers: true # Include OpenPGP-Header into emails? include_openpgp_header: true # Preferred way to receive emails to note in OpenPGP-Header # ('sign'|'encrypt'|'signencrypt'|'unprotected'|'none') openpgp_header_preference: signencrypt # Maximum size of emails allowed on the list, in kilobyte. All others will be # bounced. max_message_size_kb: 10240 # How verbose to log on the list-level (Notifications will be sent to # list-admins)? Error, warn, info, or debug. log_level: warn # How many logfiles to keep, including the current one. # Logfiles are rotated daily, so 2 means: delete logfiles older than # yesterday. Values lower than 1 are ignored. logfiles_to_keep: 2 # Which language to use for automated replies, error-messages, etc. # Available: en, de. language: en # Forward a raw copy of all incoming emails to the list-admins? # Mainly useful for debugging. forward_all_incoming_to_admins: false # Should e-mails be delivered to the original subscribed sender? # Disabling this only works for signed e-mails; any e-mail that is unsigned # sent to the list is treated as coming from an unknown source deliver_selfsent: true # Set reply-to header to original sender's reply-to? # Enabling this will set the reply-to-header of emails sent by schleuder # to the original sender's reply-to-header. If the original sender # did not supply a reply-to-header, the original from-header will be used. # This option can enabled for improved usability since this affect # mail client's reply-to button to reply to the original sender instead of # the whole list. set_reply_to_to_sender: false # Munge from-header? # Enabling this option will add the original sender to the from-header. # To avoid DMARC issues, we still use the list's address as from-address. # However the sender's address will be included as displayed name. # For example: "sender@sender.org via list@list.org" # This option can enabled for improved usability since this affect # mail client's displayed name. munge_from: false # If enabled, parse Autocrypt-header and look for attached keys in incoming emails. # For each found key (a), check if it contains a UID matching the From-header. In # case it does not, skip this key and handle the next, if any. # Lookup fingerprint of key (a) and check if it's already present in the keyring. # If present, import the key (a). # Otherwise, check if a key, which contains a UID matching the From-header, is present # in the keyring. # If not, import the key (a). # Before doing any import, ensure via a filter so that only the relevant UID is # kept. # Please be aware that this might result in a newly imported key that matches a # subscriber's email address, which didn't originate from the subscriber (in case # of forged From-header). We consider this not a serious problem because that key # will not be used for that subscriber's emails unless it is assigned manually. key_auto_import_from_email: false schleuder-5.0.1/etc/postfix/000077500000000000000000000000001502127241700157435ustar00rootroot00000000000000schleuder-5.0.1/etc/postfix/schleuder_sqlite.cf000066400000000000000000000022111502127241700216100ustar00rootroot00000000000000# Use this as a table for postfix to select addresses that schleuder # thinks belong to it. This is useful when # smtpd_reject_unlisted_recipient = yes (which is the default for # modern Postfix) # For example, you might dedicate Postfix's "virtual" domains to # schleuder with the following set of configs in main.cf: # # virtual_domains = lists.example.org # virtual_transport = schleuder # virtual_alias_maps = hash:/etc/postfix/virtual_aliases # virtual_mailbox_maps = sqlite:/etc/postfix/schleuder_sqlite.cf # schleuder_destination_recipient_limit = 1 # it is not recommended to use this table for more powerful # configuration options (e.g. transport_maps) because it could give # the schleuder user (which can write the given sqlite database) the # power to change settings for other mail handled by this Postfix # instance. dbpath = /var/lib/schleuder/db.sqlite query = select 'present' from lists where email = '%s' or email = replace('%s', '-bounce@', '@') or email = replace('%s', '-owner@', '@') or email = replace('%s', '-request@', '@') or email = replace('%s', '-sendkey@', '@') schleuder-5.0.1/etc/schleuder-api-daemon.service000066400000000000000000000002731502127241700216210ustar00rootroot00000000000000[Unit] Description=Schleuder API daemon After=local-fs.target network.target [Service] ExecStart=/usr/local/bin/schleuder-api-daemon User=schleuder [Install] WantedBy=multi-user.target schleuder-5.0.1/etc/schleuder-weekly-key-maintenance.service000066400000000000000000000003361502127241700241550ustar00rootroot00000000000000[Unit] Description=Schleuder weekly key maintenance After=local-fs.target network.target [Service] Type=oneshot ExecStart=/usr/local/bin/schleuder refresh_keys ExecStart=/usr/local/bin/schleuder check_keys User=schleuder schleuder-5.0.1/etc/schleuder-weekly-key-maintenance.timer000066400000000000000000000002011502127241700236240ustar00rootroot00000000000000[Unit] Description=Schleuder weekly key maintenance [Timer] OnCalendar=weekly Persistent=true [Install] WantedBy=timers.target schleuder-5.0.1/etc/schleuder.cron.weekly000066400000000000000000000002731502127241700204110ustar00rootroot00000000000000#!/bin/sh test -x /usr/local/bin/schleuder || exit 0 su -s /bin/sh schleuder -c "/usr/local/bin/schleuder refresh_keys" su -s /bin/sh schleuder -c "/usr/local/bin/schleuder check_keys" schleuder-5.0.1/etc/schleuder.yml000066400000000000000000000071161502127241700167550ustar00rootroot00000000000000# Where are the list-directories stored (contain log-files and GnuPG-keyrings). lists_dir: /var/lib/schleuder/lists # Where to write list-logs. The actual log-file will be ///list.log. listlogs_dir: /var/lib/schleuder/lists # Schleuder looks for additional, custom keyword-handlers in this directory. keyword_handlers_dir: /usr/local/lib/schleuder/keyword_handlers # Schleuder reads filters also from this directory path, # in the specific pre_decryption or post_decryption subdirectory. # Filter files must follow the following convention for the # filename: \d+_a_name.rb # Where \d+ is any number, that defines the place in the # list of filters and a_name must match the method name # of the filter. # The built-in filters are using round numbers for their # positioning within the list. Increased by ten. filters_dir: /usr/local/lib/schleuder/filters # How verbose should Schleuder log to syslog? (list-specific messages are written to the list's log-file). log_level: warn # Which verifying keyserver to fetch keys from? # This server must support the VKS API. # To disable lookup via this type of keyserver, set this to a blank value. # Note: This must include the procotol scheme (e.g. "https://"). vks_keyserver: https://keys.openpgp.org # Which traditional keyserver to fetch keys from? # This server must support the SKS API. # Please consider that public SKS servers don't verify any upload, the keys # they deliver should not be trusted without additional verification. This is # important if you e.g. allow keys to be fetched automatically, or by email # address. # This keyserver is queried only if the vks_keyserver didn't have a key. # Note: This must include the procotol scheme (e.g. "https://"). # Beware: Only specify https here if the server does use a commonly accepted # TLS certificate. Most servers of the SKS-pool do not! sks_keyserver: # Should Schleuder use a proxy for HTTP requests (to fetch OpenPGP keys)? # To route HTTP requests via Tor set up a local Tor daemon and enter e.g. # socks5h://127.0.0.1:9050 (for a typical Tor service). #http_proxy: # Who is maintaining the overall schleuder installation and should be # notified about severe problems with lists. # This address should be a postmaster-like account, especially it should # not be another schleuder list. # Is also used as an envelope sender of admin notifications. superadmin: root@localhost # Umask with which to create directories and files. The default (0077) lets # only the owners read them, no one else. # Only change this in special cases and if you know what you are doing! # (Be careful to retain the value type, it must not be quoted and it must start # with a zero!) umask: 0077 # For these options see documentation for ActionMailer::smtp_settings, e.g. . smtp_settings: address: localhost port: 25 #domain: #enable_starttls_auto: #openssl_verify_mode: #authentication: #user_name: #password: # The database to use. Unless you want to run the tests you only need the `production`-section. database: production: adapter: 'sqlite3' database: /var/lib/schleuder/db.sqlite pragmas: journal_mode: 'delete' timeout: 5000 api: host: localhost port: 4443 # Certificate and key to use. You can create new ones with `schleuder cert generate`. tls_cert_file: /etc/schleuder/schleuder-certificate.pem tls_key_file: /etc/schleuder/schleuder-private-key.pem # List of api_keys to allow access to the API. # Example: # valid_api_keys: # - abcdef... # - zyxwvu... valid_api_keys: schleuder-5.0.1/lib/000077500000000000000000000000001502127241700142425ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder-api-daemon.rb000077500000000000000000000034021502127241700205570ustar00rootroot00000000000000#!/usr/bin/env ruby # Make sinatra use production as default-environment ENV['RACK_ENV'] ||= 'production' $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__)) require 'sinatra/base' require 'sinatra/json' require 'sinatra/namespace' require 'thin' require 'schleuder' require 'schleuder-api-daemon/routes/status' require 'schleuder-api-daemon/routes/version' require 'schleuder-api-daemon/routes/list' require 'schleuder-api-daemon/routes/subscription' require 'schleuder-api-daemon/routes/key' require 'schleuder-api-daemon/helpers/schleuder-api-daemon-helper' %w[tls_cert_file tls_key_file].each do |config_key| path = Conf.api[config_key] if ! File.readable?(path) $stderr.puts "Error: '#{path}' is not a readable file (from #{config_key} in config)." exit 1 end end class SchleuderApiDaemon < Sinatra::Base helpers SchleuderApiDaemonHelper configure do set :server, :thin set :port, Schleuder::Conf.api['port'] || 4443 set :bind, Schleuder::Conf.api['host'] || 'localhost' if settings.development? set :logging, Logger::DEBUG else set :logging, Logger::WARN end end before do authenticate! cast_param_values end after do # Return connection to pool after each request. ActiveRecord::Base.connection.close end error do exc = env['sinatra.error'] logger.error "Error: #{env['sinatra.error'].message}" case exc when Errno::EACCES server_error(exc.message) else client_error(exc.to_s) end end error 404 do 'Not found' end def self.run! super do |server| server.ssl = true server.ssl_options = { :cert_chain_file => Conf.api['tls_cert_file'], :private_key_file => Conf.api['tls_key_file'] } end end end schleuder-5.0.1/lib/schleuder-api-daemon/000077500000000000000000000000001502127241700202305ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder-api-daemon/helpers/000077500000000000000000000000001502127241700216725ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb000066400000000000000000000102041502127241700274570ustar00rootroot00000000000000module SchleuderApiDaemonHelper def valid_credentials? @auth ||= Rack::Auth::Basic::Request.new(request.env) if @auth.provided? && @auth.basic? && @auth.credentials.present? username, api_key = @auth.credentials username == 'schleuder' && Conf.api_valid_api_keys.include?(api_key) else false end end def authenticate! # Be careful to use path_info() — it can be changed by other filters! return if request.path_info == '/status.json' if ! valid_credentials? headers['WWW-Authenticate'] = 'Basic realm="Schleuder API Daemon"' halt 401, "Not authorized\n" end end def list(id_or_email=nil) if id_or_email.blank? if params[:list_id].present? id_or_email = params[:list_id] else client_error 'Parameter list_id is required' end end if is_an_integer?(id_or_email) list = List.where(id: id_or_email).first else # list_id is actually an email address list = List.where(email: id_or_email).first end list || halt(404) end def subscription(id_or_email) if is_an_integer?(id_or_email) sub = Subscription.where(id: id_or_email.to_i).first else # Email if params[:list_id].blank? client_error 'Parameter list_id is required when using email as identifier for subscriptions.' else sub = list.subscriptions.where(email: id_or_email).first end end sub || halt(404) end def requested_list_id # ActiveResource doesn't want to use query-params with create(), so here # list_id might be included in the request-body. params['list_id'] || parsed_body['list_id'] || client_error('Need list_id') end def parsed_body @parsed_body ||= begin b = JSON.parse(request.body.read) logger.debug "parsed body: #{b.inspect}" b end end def server_error(msg) logger.warn msg halt(500, json(error: msg)) end # TODO: unify error messages. This method currently sends an old error format. See . def client_error(obj_or_msg, http_code=400) text = case obj_or_msg when String, Symbol obj_or_msg.to_s when ActiveRecord::Base obj_or_msg.errors.full_messages else obj_or_msg end logger.error "Sending error to client: #{text.inspect}" halt(http_code, json(errors: text)) end # poor persons type casting def cast_param_values params.each do |key, value| params[key] = case value when 'true' then true when 'false' then false when '0' then 0 when is_an_integer?(value) then value.to_i else value end end end def key_to_hash(key, include_keydata=false) hash = { fingerprint: key.fingerprint, email: key.email, expiry: key.expires, generated_at: key.generated_at, primary_uid: key.primary_uid.uid, summary: key.summary, trust_issues: key.usability_issue } if include_keydata hash[:description] = key.to_s hash[:ascii] = key.armored end hash end def set_x_messages(messages) if messages.present? headers 'X-Messages' => Array(messages).join(' // ').gsub(/\n/, ' // ') end end def find_key_material key_material = parsed_body['key_material'].presence # By convention key_material is either ASCII or base64-encoded. if key_material && ! key_material.match('BEGIN PGP') key_material = Base64.decode64(key_material) end key_material end def find_attributes_from_body(attribs) Array(attribs).inject({}) do |memo, attrib| if parsed_body.has_key?(attrib) memo[attrib] = parsed_body[attrib] end memo end end def is_an_integer?(input) input.to_s.match(/^[0-9]+$/).present? end end schleuder-5.0.1/lib/schleuder-api-daemon/routes/000077500000000000000000000000001502127241700215515ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder-api-daemon/routes/key.rb000066400000000000000000000033631502127241700226730ustar00rootroot00000000000000class SchleuderApiDaemon < Sinatra::Base register Sinatra::Namespace namespace '/keys' do get '.json' do keys = list.keys.sort_by(&:email).map do |key| key_to_hash(key) end json keys end post '.json' do input = parsed_body['keymaterial'] if ! input.match('BEGIN PGP') input = Base64.decode64(input) end @list = list(requested_list_id) import_result = @list.import_key(input) keys = [] messages = [] import_result.imports.each do |import_status| if import_status.action == 'error' messages << "The key with the fingerprint #{import_status.fingerprint} could not be imported for unknown reasons" else key = @list.gpg.find_distinct_key(import_status.fingerprint) if key keys << key_to_hash(key).merge({import_action: import_status.action}) end end end set_x_messages(messages) # Use a Hash as single response object to stay more REST-like. (also, # ActiveResource chokes if we return an array here). # The 'type' attribute is only necessary to keep ActiveResource from # complaining about "expected attributes to be able to convert to Hash", # which for some reason is raised without it (or some other, similar # attribute). json({type: 'keys', keys: keys}) end get '/check_keys.json' do json result: list.check_keys end get '/:fingerprint.json' do |fingerprint| if key = list.key(fingerprint) json key_to_hash(key, true) else 404 end end delete '/:fingerprint.json' do |fingerprint| if list.delete_key(fingerprint) 200 else 404 end end end end schleuder-5.0.1/lib/schleuder-api-daemon/routes/list.rb000066400000000000000000000030051502127241700230470ustar00rootroot00000000000000class SchleuderApiDaemon < Sinatra::Base register Sinatra::Namespace namespace '/lists' do get '.json' do json List.all, include: :subscriptions end post '.json' do listname = parsed_body['email'] fingerprint = parsed_body['fingerprint'] adminaddress = parsed_body['adminaddress'] adminfingerprint = parsed_body['adminfingerprint'] adminkey = parsed_body['adminkey'] list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminfingerprint, adminkey).run if list.nil? client_error(messages, 422) elsif ! list.valid? client_error(list, 422) else set_x_messages(messages) body json(list) end end get '/configurable_attributes.json' do json(List.configurable_attributes) + "\n" end post '/send_list_key_to_subscriptions.json' do json(result: list.send_list_key_to_subscriptions) end get '/new.json' do json List.new end get '/:id.json' do |id| json list(id) end put '/:id.json' do |id| list = list(id) if list.update(parsed_body) 204 else client_error(list) end end patch '/:id.json' do |id| list = list(id) if list.update(parsed_body) 204 else client_error(list) end end delete '/:id.json' do |id| list = list(id) if list.destroy 200 else client_error(list) end end end end schleuder-5.0.1/lib/schleuder-api-daemon/routes/status.rb000066400000000000000000000001401502127241700234140ustar00rootroot00000000000000class SchleuderApiDaemon < Sinatra::Base get '/status.json' do json status: :ok end end schleuder-5.0.1/lib/schleuder-api-daemon/routes/subscription.rb000066400000000000000000000052301502127241700246220ustar00rootroot00000000000000class SchleuderApiDaemon < Sinatra::Base register Sinatra::Namespace namespace '/subscriptions' do get '.json' do filterkeys = Subscription.configurable_attributes + ['list_id', 'email'] filter = params.select do |param| filterkeys.include?(param) end logger.debug "Subscription filter: #{filter.inspect}" if filter['list_id'] && ! is_an_integer?(filter['list_id']) # Value is an email-address if list = List.where(email: filter['list_id']).first filter['list_id'] = list.id else status 404 return json(errors: 'No such list') end end json Subscription.where(filter) end post '.json' do begin list = list(requested_list_id) # We don't have to care about nil-values, subscribe() does that for us. sub, msgs = list.subscribe( parsed_body['email'], parsed_body['fingerprint'], parsed_body['admin'], parsed_body['delivery_enabled'], find_key_material ) set_x_messages(msgs) logger.debug "subcription: #{sub.inspect}" if sub.valid? logger.debug "Subscribed: #{sub.inspect}" # TODO: why redirect instead of respond with result? redirect to("/subscriptions/#{sub.id}.json"), 201 else client_error(sub, 422) end rescue ActiveRecord::RecordNotUnique logger.error 'Already subscribed' status 422 json errors: {email: ['is already subscribed']} end end get '/configurable_attributes.json' do json(Subscription.configurable_attributes) + "\n" end get '/new.json' do json Subscription.new end get '/:id.json' do |id| json subscription(id) end put '/:id.json' do |id| sub = subscription(id) list = sub.list args = find_attributes_from_body(%w[email fingerprint admin delivery_enabled]) fingerprint, messages = list.import_key_and_find_fingerprint(find_key_material) set_x_messages(messages) # For an already existing subscription, only update fingerprint if a # new one has been selected from the upload. if fingerprint.present? args['fingerprint'] = fingerprint end if sub.update(args) 200 else client_error(sub, 422) end end patch '/:id.json' do |id| sub = subscription(id) if sub.update(parsed_body) 200 else client_error(sub) end end delete '/:id.json' do |id| if sub = subscription(id).destroy 200 else client_error(sub) end end end end schleuder-5.0.1/lib/schleuder-api-daemon/routes/version.rb000066400000000000000000000001611502127241700235610ustar00rootroot00000000000000class SchleuderApiDaemon < Sinatra::Base get '/version.json' do json version: Schleuder::VERSION end end schleuder-5.0.1/lib/schleuder.rb000066400000000000000000000051521502127241700165500ustar00rootroot00000000000000# default to UTF-8 encoding as early as possible for external # data. # # this should ensure we are able to parse most incoming # plain text mails in different charsets. Encoding.default_external = Encoding::UTF_8 # Stdlib require 'etc' require 'fileutils' require 'singleton' require 'yaml' require 'pathname' require 'syslog/logger' require 'logger' require 'open3' require 'socket' require 'base64' # Require mandatory libs. The database-layer-lib is required below. require 'mail' require 'gpgme' require 'active_record' require 'active_support' require 'active_support/core_ext/string' require 'typhoeus' # Load schleuder libdir = Pathname.new(__FILE__).dirname.realpath rootdir = libdir.dirname $:.unshift libdir # Monkeypatches require 'schleuder/mail/parts_list.rb' require 'schleuder/mail/message.rb' require 'schleuder/mail/gpg.rb' require 'schleuder/gpgme/import_status.rb' require 'schleuder/gpgme/key.rb' require 'schleuder/gpgme/sub_key.rb' require 'schleuder/gpgme/ctx.rb' require 'schleuder/gpgme/user_id.rb' require 'schleuder/gpgme/key_extractor' # The Code[tm] require 'schleuder/errors/base' Dir["#{libdir}/schleuder/errors/*.rb"].each do |file| require file end # Load schleuder/conf before the other classes, it defines constants! require 'schleuder/conf' require 'schleuder/version' require 'schleuder/http' require 'schleuder/key_fetcher' require 'schleuder/vks_client' require 'schleuder/sks_client' require 'schleuder/logger_notifications' require 'schleuder/logger' require 'schleuder/listlogger' require 'schleuder/keyword_handlers_runner' require 'schleuder/keyword_handlers/base' Dir["#{libdir}/schleuder/keyword_handlers/*.rb"].each do |file| require file end require 'schleuder/filters_runner' Dir["#{libdir}/schleuder/validators/*.rb"].each do |file| require file end require 'schleuder/runner' require 'schleuder/list' require 'schleuder/list_builder' require 'schleuder/subscription' require 'schleuder/email_key_importer' # Setup ENV['SCHLEUDER_CONFIG'] ||= '/etc/schleuder/schleuder.yml' ENV['SCHLEUDER_LIST_DEFAULTS'] ||= '/etc/schleuder/list-defaults.yml' ENV['SCHLEUDER_ENV'] ||= 'production' ENV['SCHLEUDER_ROOT'] = rootdir.to_s GPGME::Ctx.set_gpg_path_from_env GPGME::Ctx.check_gpg_version # TODO: Test if database is writable if sqlite. ActiveRecord::Base.establish_connection(Schleuder::Conf.database) ActiveRecord::Base.logger = Schleuder.logger Mail.defaults do delivery_method :smtp, Schleuder::Conf.smtp_settings.symbolize_keys end I18n.load_path += Dir["#{rootdir}/locales/*.yml"] I18n.enforce_available_locales = true I18n.default_locale = :en File.umask(Schleuder::Conf.umask) include Schleuder schleuder-5.0.1/lib/schleuder/000077500000000000000000000000001502127241700162205ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/cli.rb000066400000000000000000000157161502127241700173260ustar00rootroot00000000000000require 'thor' require 'yaml' require 'gpgme' require 'charlock_holmes' require_relative '../schleuder' require 'schleuder/cli/subcommand_fix' require 'schleuder/cli/cli_helper' require 'schleuder/cli/schleuder_cert_manager' require 'schleuder/cli/cert' module Schleuder class Cli < Thor include CliHelper register(Cert, 'cert', 'cert ...', 'Generate TLS-certificate and show fingerprint') map '-v' => :version map '--version' => :version desc 'version', 'Show version of schleuder' def version say Schleuder::VERSION end desc 'new_api_key', 'Generate a new API key to be used by a client.' def new_api_key require 'securerandom' puts SecureRandom.hex(32) end desc 'work list@hostname < message', 'Run a message through a list.' def work(listname) message = STDIN.read error = Schleuder::Runner.new.run(message, listname) if error.kind_of?(StandardError) fatal error end rescue => exc begin Schleuder.logger.fatal(exc.message_with_backtrace, message) # Don't use FatalError here to reduce dependency on other code. say I18n.t('errors.fatalerror') rescue => e # Give users a clue what to do in case everything blows up. # As apparently even the logging raised exceptions we can't even store # any information in the logs. fatal "A serious, unhandleable error happened. Please contact the administrators of this system or service and provide them with the following information:\n\n#{e.message}" end exit 1 end desc 'check_keys', 'Check all lists for unusable or expiring keys and send the results to the list-admins. (This is supposed to be run from cron or systemd weekly.)' def check_keys List.all.each do |list| I18n.locale = list.language text = list.check_keys if text && ! text.empty? msg = "#{I18n.t('check_keys_intro', email: list.email)}\n\n#{text}" list.logger.notify_admin(msg, nil, I18n.t('check_keys')) end end permission_notice end desc 'refresh_keys [list1@example.com]', 'Refresh all keys of all list from the keyservers sequentially (one by one or on the passed list). (This is supposed to be run from cron or systemd weekly.)' def refresh_keys(list=nil) work_on_lists(:refresh_keys, list) permission_notice end desc 'install', 'Set-up or update Schleuder environment (create folders, copy files, fill the database).' def install config_dir = Pathname.new(ENV['SCHLEUDER_CONFIG']).dirname root_dir = Pathname.new(ENV['SCHLEUDER_ROOT']) # Check if lists_dir contains v2-data. if Dir.glob("#{Conf.lists_dir}/*/*/members.conf").size > 0 msg = "Lists directory #{Conf.lists_dir} appears to contain data from a Schleuder version 2.x installation.\nPlease remove this data and retry the installation. Schleuder version 4 doesn't support migrating these old lists, in case you need to, please install Schleuder version 3 first." fatal msg, 2 end [Conf.keyword_handlers_dir, Conf.lists_dir, Conf.listlogs_dir, config_dir].each do |dir| dir = Pathname.new(dir) if ! dir.exist? begin dir.mkpath rescue Errno::EACCES => exc problem_dir = exc.message.split(' - ').last fatal "Cannot create required directory due to lacking write permissions: #{problem_dir}.\nPlease fix the permissions or create the directory manually and then run this command again." end end end Pathname.glob(root_dir.join('etc').join('*.yml')).each do |file| target = config_dir.join(file.basename) if ! target.exist? if target.dirname.writable? FileUtils.cp file, target else fatal "Cannot copy default config file due to lacking write permissions, please copy manually and then run this command again:\n#{file.realpath} → #{target}" end end end # If the table for lists exists we assume the DB has been initialized before. if List.table_exists? say shellexec("cd #{root_dir} && rake db:migrate") else say shellexec("cd #{root_dir} && rake db:init") if Conf.database['adapter'].match(/sqlite/) say "NOTE: The database was prepared using sqlite. If you prefer to use a different DBMS please edit the 'database'-section in /etc/schleuder/schleuder.yml, create the database, install the corresponding ruby-library (e.g. `gem install mysql`) and run this current command again" end end if ! File.exist?(Conf.api['tls_cert_file']) || ! File.exist?(Conf.api['tls_key_file']) Schleuder::Cert.new.generate end if Conf.umask == 0077 # The umask is set to the (new) default, let's have a look at the list-dirs. list_dirs = Dir.glob("#{Conf.lists_dir}/*") list_dirs.each do |list_dir| perm_bits = File.stat(list_dir).mode.to_s(8)[-3..] if perm_bits != '700' say "WARNING: The directory '#{list_dir}' has permissions that do not match the umask as set in schleuder's config. You should probably fix that by running `chmod -R g-rwx #{list_dir}`." end end end say "Schleuder has been set up. You can now create a new list using `schleuder-cli`.\nWe hope you enjoy!" permission_notice rescue => exc fatal exc.message end no_commands do def shellexec(cmd) result = `#{cmd} 2>&1` if $?.exitstatus > 0 fatal result, $?.exitstatus end result end end private def work_on_lists(subj, list=nil) if list.nil? selected_lists = List.all else selected_lists = List.where(email: list) if selected_lists.blank? error("No list with this address exists: #{list.inspect}") end end selected_lists.each do |list| I18n.locale = list.language output = list.send(subj) if output.present? msg = "#{I18n.t("#{subj}_intro", email: list.email)}\n\n#{output}" list.logger.notify_admin(msg, nil, I18n.t(subj)) end end end # Make this class exit with code 1 in case of an error. See . def self.exit_on_failure? true end def permission_notice if Process.euid == 0 dirs = [Conf.lists_dir, Conf.listlogs_dir] if Conf.database['adapter'] == 'sqlite3' dirs << Conf.database['database'] end dirs_sentence = dirs.uniq.map { |dir| enquote(dir) }.to_sentence say "Warning: this process was run as root -- please make sure that all files in #{dirs_sentence} have correct file system permissions for the user that is running both, schleuder from the MTA and `schleuder-api-daemon`." end end def enquote(string) "\`#{string}\`" end end end schleuder-5.0.1/lib/schleuder/cli/000077500000000000000000000000001502127241700167675ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/cli/cert.rb000066400000000000000000000017051502127241700202540ustar00rootroot00000000000000module Schleuder class Cert < Thor extend SubcommandFix desc 'generate', 'Generate a new TLS-certificate.' def generate key = Conf.api['tls_key_file'] cert = Conf.api['tls_cert_file'] fingerprint = SchleuderCertManager.generate('schleuder', key, cert) puts "Fingerprint of generated certificate: #{fingerprint}" puts 'Have this fingerprint included into the configuration-file of all clients that want to connect to your Schleuder API.' if Process.euid == 0 puts '! Warning: this process was run as root — please make sure the above files are accessible by the user that is running `schleuder-api-daemon`.' end end desc 'fingerprint', 'Show fingerprint of configured certificate.' def fingerprint cert = Conf.api['tls_cert_file'] fingerprint = SchleuderCertManager.fingerprint(cert) say "Fingerprint of #{Conf.api['tls_cert_file']}: #{fingerprint}" end end end schleuder-5.0.1/lib/schleuder/cli/cli_helper.rb000066400000000000000000000003401502127241700214170ustar00rootroot00000000000000module Schleuder module CliHelper def self.included(base) base.no_commands do def fatal(msg, exitcode=1) error("Error: #{msg}") exit exitcode end end end end end schleuder-5.0.1/lib/schleuder/cli/schleuder_cert_manager.rb000066400000000000000000000046431502127241700240100ustar00rootroot00000000000000require 'openssl' require 'pathname' class SchleuderCertManager def self.generate(project_name, filename_key, filename_cert) keysize = 2048 subject = "/C=MW/O=Schleuder/OU=#{project_name}" filename_key = Pathname.new(filename_key).expand_path filename_cert = Pathname.new(filename_cert).expand_path key = OpenSSL::PKey::RSA.new(keysize) cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.parse(subject) cert.issuer = cert.subject cert.not_before = Time.now cert.not_after = Time.now + 10 * 365 * 24 * 60 * 60 cert.public_key = key.public_key cert.serial = 0x0 cert.version = 2 ef = OpenSSL::X509::ExtensionFactory.new ef.subject_certificate = cert ef.issuer_certificate = cert cert.extensions = [ ef.create_extension('basicConstraints', 'CA:TRUE', true), ef.create_extension('subjectKeyIdentifier', 'hash'), ] cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid:always,issuer:always') cert.sign key, OpenSSL::Digest::SHA256.new filename_key = prepare_writing(filename_key) filename_cert = prepare_writing(filename_cert) filename_key.open('w', 400) do |fd| fd.puts key end puts "Private key written to: #{filename_key}" filename_cert.open('w') do |fd| fd.puts cert.to_pem end puts "Certificate written to: #{filename_cert}" fingerprint(cert) rescue => exc error exc.message end def self.fingerprint(cert) if ! cert.is_a?(OpenSSL::X509::Certificate) path = Pathname.new(cert).expand_path if ! path.readable? error "Error: Not a readable file: #{path}" end cert = OpenSSL::X509::Certificate.new(path.read) end OpenSSL::Digest::SHA256.new(cert.to_der).to_s end def self.error(msg) $stderr.puts "Error: #{msg}" exit 1 end def self.note(msg) $stdout.puts "Note: #{msg}" end def self.prepare_writing(filename) if filename.exist? note "File exists: #{filename} — writing to current directory, you should move the file manually or change the configuration file." if filename.basename.exist? error "File exists: #{filename.basename} — (re)move it or fix previous error and try again." end filename = filename.basename end if ! filename.dirname.exist? filename.dirname.mkpath end filename end end schleuder-5.0.1/lib/schleuder/cli/subcommand_fix.rb000066400000000000000000000005071502127241700223140ustar00rootroot00000000000000module Schleuder module SubcommandFix # Fixing a bug in Thor where the actual subcommand wouldn't show up # with some invocations of the help-output. def banner(task, namespace = true, subcommand = true) "#{basename} #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}" end end end schleuder-5.0.1/lib/schleuder/conf.rb000066400000000000000000000073161502127241700175010ustar00rootroot00000000000000require 'erb' module Schleuder class Conf include Singleton EMAIL_REGEXP = URI::MailTo::EMAIL_REGEXP # TODO: drop v3 keys and only accept length of 40 FINGERPRINT_REGEXP = /\A(0x)?[a-f0-9]{32}([a-f0-9]{8})?\z/i DEFAULTS = { 'lists_dir' => '/var/lib/schleuder/lists', 'umask' => 0077, 'listlogs_dir' => '/var/lib/schleuder/lists', 'keyword_handlers_dir' => '/usr/local/lib/schleuder/keyword_handlers', 'filters_dir' => '/usr/local/lib/schleuder/filters', 'log_level' => 'warn', 'superadmin' => 'root@localhost', 'vks_keyserver' => 'https://keys.openpgp.org', 'sks_keyserver' => 'http://pool.sks-keyservers.net', 'http_proxy' => '', 'smtp_settings' => { 'address' => 'localhost', 'port' => 25, 'domain' => 'localhost', 'enable_starttls_auto' => true, # Don't verify by default because most smtp servers don't include # 'localhost' into their TLS-certificates. 'openssl_verify_mode' => 'none', 'authentication' => nil, 'user_name' => nil, 'password' => nil, }, 'database' => { 'production' => { 'adapter' => 'sqlite3', 'database' => '/var/lib/schleuder/db.sqlite', 'timeout' => 5000 } }, 'api' => { 'host' => 'localhost', 'port' => 4443, 'tls_cert_file' => '/etc/schleuder/schleuder-certificate.pem', 'tls_key_file' => '/etc/schleuder/schleuder-private-key.pem', 'valid_api_keys' => [] } } def config @config ||= load_config(ENV['SCHLEUDER_CONFIG']) end def reload! @config = nil config end def self.lists_dir instance.config['lists_dir'] end def self.umask instance.config['umask'] end def self.listlogs_dir instance.config['listlogs_dir'] end def self.keyword_handlers_dir instance.config['keyword_handlers_dir'] end def self.filters_dir instance.config['filters_dir'] end def self.database instance.config['database'][ENV['SCHLEUDER_ENV']] end def self.databases instance.config['database'] end def self.superadmin instance.config['superadmin'] end def self.log_level instance.config['log_level'] || 'WARN' end def self.api instance.config['api'] || {} end def self.api_valid_api_keys Array(api['valid_api_keys']) end # Three legacy options def self.smtp_host instance.config['smtp_host'] end def self.smtp_port instance.config['smtp_port'] end def self.smtp_helo_domain instance.config['smtp_helo_domain'] end def self.smtp_settings settings = instance.config['smtp_settings'] || {} # Support previously used config-options. # Remove this in future versions. {smtp_host: :address, smtp_port: :port, smtp_helo_domain: :domain}.each do |old, new| value = self.send(old) if value.present? Schleuder.logger.warn "Deprecation warning: In schleuder.yml #{old} should be changed to smtp_settings[#{new}]." settings[new] = value end end settings end def self.vks_keyserver instance.config['vks_keyserver'] end def self.sks_keyserver instance.config['sks_keyserver'] end def self.http_proxy instance.config['http_proxy'] end private def load_config(filename) DEFAULTS.deep_merge(load_config_file(filename)) end def load_config_file(filename) file = Pathname.new(filename) if file.readable? YAML.load(ERB.new(file.read).result) else {} end end end end schleuder-5.0.1/lib/schleuder/email_key_importer.rb000066400000000000000000000072561502127241700224370ustar00rootroot00000000000000module Schleuder class EmailKeyImporter class << self def import_from_attachments(list, mail) # Shouldn't happen, but who knows... return if ! mail.from.first.match(Conf::EMAIL_REGEXP) mail.attachments.map do |part| if part.content_type == 'application/pgp-keys' filter_and_maybe_import_keys(mail, part.body.decoded) end end.compact end def import_from_autocrypt_header(list, mail) # Shouldn't happen, but who knows... return if ! mail.from.first.match(Conf::EMAIL_REGEXP) return if mail.header['Autocrypt'].blank? keydata_base64 = mail.header['Autocrypt'].to_s.split('keydata=', 2)[1] return if keydata_base64.blank? keydata = Base64.decode64(keydata_base64) return if keydata.blank? filter_and_maybe_import_keys(mail, keydata) end def filter_and_maybe_import_keys(mail, keydata) extracted_keys = GPGME::KeyExtractor.extract_by_email_address(mail.from.first, keydata) extracted_keys.map do |fingerprint, filtered_keydata| maybe_import_key(mail, fingerprint, filtered_keydata) end.compact end def maybe_import_key(mail, fingerprint, filtered_keydata) if mail.list.keys(fingerprint).size == 1 return update_key(mail, filtered_keydata) end if mail.list.keys(mail.from.first).size > 0 mail.add_pseudoheader('Note', I18n.t('email_key_importer.key_already_present')) return end add_key(mail, filtered_keydata) end def add_key(mail, keydata) fingerprint, import_states = import_to_list(mail, keydata) return if ! fingerprint if ! import_states.include?(I18n.t('import_states.new_key')) mail.list.logger.error "Importing key failed! Fingerprint: #{fingerprint.inspect} -- Import-states: #{import_states.inspect}" mail.add_pseudoheader('Note', I18n.t('email_key_importer.import_error', fingerprint: fingerprint.inspect, import_states: import_states.inspect)) return end key = mail.list.keys(fingerprint).first mail.add_pseudoheader('Note', I18n.t('email_key_importer.key_added', key_summary: key.summary)) fingerprint end def update_key(mail, keydata) fingerprint, import_states = import_to_list(mail, keydata) return if ! fingerprint key = mail.list.keys(fingerprint).first if import_states == ['unchanged'] mail.add_pseudoheader('Note', I18n.t('email_key_importer.key_unchanged', key_summary: key.summary)) return end key = mail.list.keys(fingerprint).first mail.add_pseudoheader('Note', I18n.t('email_key_importer.key_updated', key_summary: key.summary)) fingerprint end def import_to_list(mail, keydata) result = mail.list.gpg.import_filtered(keydata) # At this point we expect the keydata to only contain one key, and thus # only one key and value in the result. if ! result.is_a?(Hash) || result.keys.size != 1 || ! result.values.first.is_a?(Array) mail.list.logger.error "Unexpected result when importing key from temporary keyring => #{result.inspect}" mail.add_pseudoheader('Note', I18n.t('email_key_importer.technical_error')) return false end [result.keys.first, result.values.first] end end end end schleuder-5.0.1/lib/schleuder/errors/000077500000000000000000000000001502127241700175345ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/errors/active_model_error.rb000066400000000000000000000003721502127241700237270ustar00rootroot00000000000000module Schleuder module Errors class ActiveModelError < Base def initialize(errors) messages = errors.messages.map do |message| message.join(' ') end.join("\n") super messages end end end end schleuder-5.0.1/lib/schleuder/errors/base.rb000066400000000000000000000004451502127241700207760ustar00rootroot00000000000000module Schleuder module Errors class Base < StandardError def t(key, **kwargs) I18n.t(key, **kwargs) end def to_s super + t('errors.signoff') end def set_default_locale I18n.locale = I18n.default_locale end end end end schleuder-5.0.1/lib/schleuder/errors/decryption_failed.rb000066400000000000000000000004061502127241700235450ustar00rootroot00000000000000module Schleuder module Errors class DecryptionFailed < Base def initialize(list) set_default_locale super t('errors.decryption_failed', key: list.key.to_s, email: list.sendkey_address) end end end end schleuder-5.0.1/lib/schleuder/errors/fatal_error.rb000066400000000000000000000002501502127241700223560ustar00rootroot00000000000000module Schleuder module Errors class FatalError < Base def initialize end def to_s t('errors.fatalerror') end end end end schleuder-5.0.1/lib/schleuder/errors/key_adduid_failed.rb000066400000000000000000000002671502127241700234740ustar00rootroot00000000000000module Schleuder module Errors class KeyAdduidFailed < Base def initialize(errmsg) super t('errors.key_adduid_failed', errmsg: errmsg) end end end end schleuder-5.0.1/lib/schleuder/errors/key_generation_failed.rb000066400000000000000000000003401502127241700243650ustar00rootroot00000000000000module Schleuder module Errors class KeyGenerationFailed < Base def initialize(listdir, listname) super t('errors.key_generation_failed', listdir: listdir, listname: listname) end end end end schleuder-5.0.1/lib/schleuder/errors/keyword_admin_only.rb000066400000000000000000000002741502127241700237610ustar00rootroot00000000000000module Schleuder module Errors class KeywordAdminOnly < Base def initialize(keyword) super t('errors.keyword_admin_only', keyword: keyword) end end end end schleuder-5.0.1/lib/schleuder/errors/list_not_found.rb000066400000000000000000000002671502127241700231140ustar00rootroot00000000000000module Schleuder module Errors class ListNotFound < Base def initialize(recipient) super t('errors.list_not_found', email: recipient) end end end end schleuder-5.0.1/lib/schleuder/errors/list_property_missing.rb000066400000000000000000000004351502127241700245330ustar00rootroot00000000000000module Schleuder module Errors class ListPropertyMissing < Base def initialize(listdir, property) @listdir = listdir @property = property end def to_s t("errors.list_#{@property}_missing", listdir: @listdir) end end end end schleuder-5.0.1/lib/schleuder/errors/listdir_problem.rb000066400000000000000000000004101502127241700232460ustar00rootroot00000000000000module Schleuder module Errors class ListdirProblem < Base def initialize(dir, problem) problem = t("errors.listdir_problem.#{problem}") super t('errors.listdir_problem.message', dir: dir, problem: problem) end end end end schleuder-5.0.1/lib/schleuder/errors/loading_list_settings_failed.rb000066400000000000000000000004041502127241700257530ustar00rootroot00000000000000module Schleuder module Errors class LoadingListSettingsFailed < Base def initialize config_file = ENV['SCHLEUDER_LIST_DEFAULTS'] super t('errors.loading_list_settings_failed', config_file: config_file) end end end end schleuder-5.0.1/lib/schleuder/errors/message_empty.rb000066400000000000000000000003401502127241700227200ustar00rootroot00000000000000module Schleuder module Errors class MessageEmpty < Base def initialize(list) set_default_locale super t('errors.message_empty', request_address: list.request_address) end end end end schleuder-5.0.1/lib/schleuder/errors/message_not_from_admin.rb000066400000000000000000000003031502127241700245540ustar00rootroot00000000000000module Schleuder module Errors class MessageNotFromAdmin < Base def initialize set_default_locale super t('errors.message_not_from_admin') end end end end schleuder-5.0.1/lib/schleuder/errors/message_sender_not_subscribed.rb000066400000000000000000000003211502127241700261260ustar00rootroot00000000000000module Schleuder module Errors class MessageSenderNotSubscribed < Base def initialize set_default_locale super t('errors.message_sender_not_subscribed') end end end end schleuder-5.0.1/lib/schleuder/errors/message_too_big.rb000066400000000000000000000004101502127241700232020ustar00rootroot00000000000000module Schleuder module Errors class MessageTooBig < Base def initialize(list) set_default_locale allowed_size = list.max_message_size_kb super t('errors.message_too_big', allowed_size: allowed_size) end end end end schleuder-5.0.1/lib/schleuder/errors/message_unauthenticated.rb000066400000000000000000000003071502127241700247520ustar00rootroot00000000000000module Schleuder module Errors class MessageUnauthenticated < Base def initialize set_default_locale super t('errors.message_unauthenticated') end end end end schleuder-5.0.1/lib/schleuder/errors/message_unencrypted.rb000066400000000000000000000002771502127241700241330ustar00rootroot00000000000000module Schleuder module Errors class MessageUnencrypted < Base def initialize set_default_locale super t('errors.message_unencrypted') end end end end schleuder-5.0.1/lib/schleuder/errors/message_unsigned.rb000066400000000000000000000002711502127241700234010ustar00rootroot00000000000000module Schleuder module Errors class MessageUnsigned < Base def initialize set_default_locale super t('errors.message_unsigned') end end end end schleuder-5.0.1/lib/schleuder/errors/standard_error.rb000066400000000000000000000001541502127241700230720ustar00rootroot00000000000000class StandardError def message_with_backtrace "#{message}\n#{self.backtrace.join("\n")}\n" end end schleuder-5.0.1/lib/schleuder/errors/too_many_keys.rb000066400000000000000000000003211502127241700227350ustar00rootroot00000000000000module Schleuder module Errors class TooManyKeys < Base def initialize(listdir, listname) super t('errors.too_many_keys', listdir: listdir, listname: listname) end end end end schleuder-5.0.1/lib/schleuder/filters/000077500000000000000000000000001502127241700176705ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/filters/post_decryption/000077500000000000000000000000001502127241700231155ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/filters/post_decryption/10_request.rb000066400000000000000000000013251502127241700254330ustar00rootroot00000000000000module Schleuder module Filters def self.request(list, mail) return if ! mail.request? list.logger.debug 'Request-message' if ! mail.was_encrypted? || ! mail.was_validly_signed? list.logger.debug 'Error: Message was not encrypted and validly signed' return Errors::MessageUnauthenticated.new end if mail.keywords.empty? output = I18n.t(:no_keywords_error) else output = KeywordHandlersRunner.run(type: :request, list: list, mail: mail) output = output.flatten.map(&:presence).compact if output.blank? output = I18n.t(:no_output_result) end end mail.reply_to_signer(output) exit end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/20_max_message_size.rb000066400000000000000000000004221502127241700272640ustar00rootroot00000000000000module Schleuder module Filters def self.max_message_size(list, mail) if (mail.raw_source.size / 1024) > list.max_message_size_kb list.logger.info 'Rejecting mail as too big' return Errors::MessageTooBig.new(list) end end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/30_forward_to_owner.rb000066400000000000000000000006711502127241700273300ustar00rootroot00000000000000module Schleuder module Filters def self.forward_to_owner(list, mail) return if ! mail.to_owner? list.logger.debug 'Forwarding addressed to -owner' mail.add_pseudoheader(:note, I18n.t(:owner_forward_prefix)) cleanmail = mail.clean_copy(true) list.admins.each do |admin| list.logger.debug "Forwarding message to #{admin}" admin.send_mail(cleanmail) end exit end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/35_key_auto_import_from_attachments.rb000066400000000000000000000014051502127241700326010ustar00rootroot00000000000000module Schleuder module Filters def self.key_auto_import_from_attachments(list, mail) # Don't run if not enabled. return if ! list.key_auto_import_from_email imported_fingerprints = EmailKeyImporter.import_from_attachments(list, mail) if imported_fingerprints.size > 0 # If the message's signature could not be validated before, re-run the # validation, because after having imported new or updated keys the # validation now might work. if mail.signature.present? && ! mail.signature.valid? # Re-validate the signature validation, now that a new key was # imported that might be the previously unknown signing key. mail.repeat_validation! end end end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb000066400000000000000000000004621502127241700276020ustar00rootroot00000000000000module Schleuder module Filters def self.receive_admin_only(list, mail) if list.receive_admin_only? && ( ! mail.was_validly_signed? || ! mail.signer.admin? ) list.logger.info 'Rejecting mail as not from admin.' return Errors::MessageNotFromAdmin.new end end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb000066400000000000000000000005061502127241700313340ustar00rootroot00000000000000module Schleuder module Filters def self.receive_authenticated_only(list, mail) if list.receive_authenticated_only? && ( ! mail.was_encrypted? || ! mail.was_validly_signed? ) list.logger.info 'Rejecting mail as unauthenticated' return Errors::MessageUnauthenticated.new end end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb000066400000000000000000000004151502127241700277630ustar00rootroot00000000000000module Schleuder module Filters def self.receive_signed_only(list, mail) if list.receive_signed_only? && ! mail.was_validly_signed? list.logger.info 'Rejecting mail as unsigned' return Errors::MessageUnsigned.new end end end end schleuder-5.0.1/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb000066400000000000000000000004241502127241700305100ustar00rootroot00000000000000module Schleuder module Filters def self.receive_encrypted_only(list, mail) if list.receive_encrypted_only? && ! mail.was_encrypted? list.logger.info 'Rejecting mail as unencrypted' return Errors::MessageUnencrypted.new end end end end 80_receive_from_subscribed_emailaddresses_only.rb000066400000000000000000000006021502127241700346500ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/filters/post_decryptionmodule Schleuder module Filters def self.receive_from_subscribed_emailaddresses_only(list, mail) if list.receive_from_subscribed_emailaddresses_only? && list.subscriptions.where(email: mail.from.first.downcase).blank? list.logger.info 'Rejecting mail as not from subscribed address.' return Errors::MessageSenderNotSubscribed.new end end end end 90_strip_html_from_alternative_if_keywords_present.rb000066400000000000000000000043331502127241700356510ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/filters/post_decryptionmodule Schleuder module Filters # If keywords are present, recurse into arbitrary levels of multipart/mixed # encapsulation. If multipart/alternative is found, remove all sub-parts # but the text/plain part (assuming that every multipart/alternative # contains exactly one text/plain). Change the content_type from # multipart/alternative to multipart/mixed. def self.strip_html_from_alternative_if_keywords_present(list, mail) # Only strip the text/html-part if keywords are present if mail.keywords.blank? then return false end return self.recursively_strip_html_from_alternative_if_keywords_present(list, mail) end def self.recursively_strip_html_from_alternative_if_keywords_present(list, mail) if mail[:content_type].blank? then return false end content_type = mail[:content_type].content_type # The multipart/alternative could hide inside an arbitrary number of # levels of multipart/mixed encapsulation. # see also: https://www.rfc-editor.org/rfc/rfc2046#section-5.1.3 if content_type == 'multipart/mixed' mail.parts.each do |part| self.recursively_strip_html_from_alternative_if_keywords_present(list, part) end return false end # inside the multipart/mixed, we only care about multipart/mixed and # multipart/alternative if content_type != 'multipart/alternative' then return false end # Inside multipart/alternative, there could be a text/html-part, or there # could be a multipart/related-part which contains the text/html-part. # Everything inside the multipart/alternative that is not text/plain # should be deleted, since it will contain keywords and we only strip # keywords from text/plain-parts. Schleuder.logger.debug 'Stripping html-part from multipart/alternative-message because it contains keywords' mail.parts.delete_if do |part| content_type = part[:content_type].content_type content_type != 'text/plain' end # NOTE: We could instead unencapsulate it. mail.content_type = 'multipart/mixed' mail.add_pseudoheader(:note, I18n.t('pseudoheaders.stripped_html_from_multialt_with_keywords')) end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/000077500000000000000000000000001502127241700227165ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/filters/pre_decryption/10_forward_bounce_to_admins.rb000066400000000000000000000005531502127241700306020ustar00rootroot00000000000000module Schleuder module Filters def self.forward_bounce_to_admins(list, mail) if mail.automated_message? list.logger.info 'Forwarding automated message to admins' list.logger.notify_admin I18n.t(:forward_automated_message_to_admins), mail.original_message, I18n.t('automated_message_subject') exit end end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/20_forward_all_incoming_to_admins.rb000066400000000000000000000004431502127241700317610ustar00rootroot00000000000000module Schleuder module Filters def self.forward_all_incoming_to_admins(list, mail) if list.forward_all_incoming_to_admins list.logger.notify_admin I18n.t(:forward_all_incoming_to_admins), mail.original_message, I18n.t('incoming_message') end end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/30_send_key.rb000066400000000000000000000011501502127241700253430ustar00rootroot00000000000000module Schleuder module Filters def self.send_key(list, mail) return if ! mail.sendkey_request? list.logger.debug 'Sending public key as reply.' out = mail.reply out.from = list.email # We're not sending to a subscribed address, so we need to specify a envelope-sender manually. out.sender = list.bounce_address out.body = I18n.t(:list_public_key_attached) out.attach_list_key!(list) # TODO: find out why the gpg-module puts all the headers into the first mime-part, too out.gpg list.gpg_sign_options out.deliver exit end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/40_fix_exchange_messages.rb000066400000000000000000000024421502127241700300670ustar00rootroot00000000000000module Schleuder module Filters # Outlook / Hotmail seems to dismantle multipart/encrypted messages and # put them again together as multipart/mixed, which is wrong and makes # it problematic to correctly detect the message as a valid pgp/mime-mail. # Here we fix the mail to be a proper pgp/mime aka. multipart/encrypted # message, so further processing will detect it properly. # This problem seems to be in fact related to the use of Microsoft # Exchange. Accordingly, check if the headers contain 'X-MS-Exchange'. # See #211, #246, #331 and #333 for background. def self.fix_exchange_messages(list, mail) if mail.header_fields.any?{|f| f.name =~ /^X-MS-Exchange-/i } && !mail[:content_type].blank? && mail[:content_type].content_type == 'multipart/mixed' && mail.parts.size > 2 && mail.parts[0][:content_type].content_type == 'text/plain' && mail.parts[0].body.to_s.blank? && mail.parts[1][:content_type].content_type == 'application/pgp-encrypted' && mail.parts[2][:content_type].content_type == 'application/octet-stream' mail.parts.delete_at(0) mail.content_type = [:multipart, :encrypted, {protocol: 'application/pgp-encrypted', boundary: mail.boundary}] end end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/50_strip_html_from_alternative.rb000066400000000000000000000011561502127241700313600ustar00rootroot00000000000000module Schleuder module Filters def self.strip_html_from_alternative(list, mail) if mail[:content_type].blank? || mail[:content_type].content_type != 'multipart/alternative' || ! mail.to_s.include?('BEGIN PGP ') return false end Schleuder.logger.debug 'Stripping html-part from multipart/alternative-message' mail.parts.delete_if do |part| part[:content_type].content_type == 'text/html' end mail.content_type = 'multipart/mixed' mail.add_pseudoheader(:note, I18n.t('pseudoheaders.stripped_html_from_multialt')) end end end schleuder-5.0.1/lib/schleuder/filters/pre_decryption/60_key_auto_import_from_autocrypt_header.rb000066400000000000000000000003501502127241700334250ustar00rootroot00000000000000module Schleuder module Filters def self.key_auto_import_from_autocrypt_header(list, mail) if list.key_auto_import_from_email EmailKeyImporter.import_from_autocrypt_header(list, mail) end end end end schleuder-5.0.1/lib/schleuder/filters_runner.rb000066400000000000000000000031651502127241700216130ustar00rootroot00000000000000module Schleuder module Filters class Runner attr_reader :list, :filter_type def initialize(list, filter_type) @list = list @filter_type = filter_type end def run(mail) filters.map do |cmd| list.logger.debug "Calling filter #{cmd}" response = Filters.send(cmd, list, mail) if stop?(response) return response end end nil end def filters @filters ||= load_filters end private def stop?(response) response.kind_of?(StandardError) end def load_filters list.logger.debug "Loading #{filter_type}_decryption filters" sorted_filters.map do |filter_name| require all_filter_files[filter_name] filter_name.split('_', 2).last end end def sorted_filters @sorted_filters ||= all_filter_files.keys.sort do |a, b| a.split('_', 2).first.to_i <=> b.split('_', 2).first.to_i end end def all_filter_files @all_filter_files ||= begin files_in_filter_dirs = Dir[*filter_dirs] files_in_filter_dirs.inject({}) do |res, file| filter_name = File.basename(file, '.rb') res[filter_name] = file res end end end def filter_dirs @filter_dirs ||= [File.join(File.dirname(__FILE__), 'filters'), Schleuder::Conf.filters_dir].map do |d| File.join(d, "#{filter_type}_decryption/[0-9]*_*.rb") end end end end end schleuder-5.0.1/lib/schleuder/gpgme/000077500000000000000000000000001502127241700173175ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/gpgme/ctx.rb000066400000000000000000000133051502127241700204440ustar00rootroot00000000000000module GPGME class Ctx IMPORT_FLAGS = { 'new_key' => 1, 'new_uids' => 2, 'new_signatures' => 4, 'new_subkeys' => 8 } # This differs from import_filtered() in that it doesn't filter the keys at # all, and that it returns the import-results themselves, not strings based # on those results. def keyimport(keydata) self.import_keys(GPGME::Data.new(keydata)) result = self.import_result result.imports.map(&:set_action) result end # TODO: find solution for I18n — could be a different language in API-clients than here! def interpret_import_result(import_result) case import_result.imports.size when 1 import_status = import_result.imports.first if import_status.action == 'error' [nil, "Key #{import_status.fpr} could not be imported!"] else [import_status.fpr, nil] end when 0 [nil, 'The given key material did not contain any keys!'] else # TODO: report import-stati of the keys? [nil, 'The given key material contained more than one key, could not determine which fingerprint to use. Please set it manually!'] end end def find_keys(input=nil, secret_only=nil) keys(normalize_key_identifier(input), secret_only) end def find_distinct_key(input=nil, secret_only=nil) keys = keys(normalize_key_identifier(input), secret_only) if keys.size == 1 keys.first else nil end end def normalize_key_identifier(input) case input when /.*?([^ <>]+@[^ <>]+).*?/ "<#{$1}>" when /^http/ input when Conf::FINGERPRINT_REGEXP "0x#{input.gsub(/^0x/, '')}" else input end end # Tell gpgme to use the given binary. def self.set_gpg_path_from_env path = ENV['GPGBIN'].to_s if ! path.empty? Schleuder.logger.debug "setting gpg to use #{path}" GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, path, ENV['GNUPGHOME']) if gpg_engine.version.nil? $stderr.puts "Error: The binary you specified doesn't provide a gpg-version." exit 1 end end end def self.sufficient_gpg_version?(required) Gem::Version.new(required) <= Gem::Version.new(gpg_engine.version) end def self.check_gpg_version if ! sufficient_gpg_version?('2.2') $stderr.puts "Error: GnuPG version >= 2.2 required.\nPlease install it and/or provide the path to the binary via the environment-variable GPGBIN.\nExample: GPGBIN=/opt/gpg2/bin/gpg ..." exit 1 end end def self.gpg_engine GPGME::Engine.info.find {|e| e.protocol == GPGME::PROTOCOL_OpenPGP } end def import_filtered(input, gpg_extra_arg='') # Import through gpgcli so we can use import-filter. GPGME still does # not provide that feature (as of summer 2023): :( gpgerr, gpgout, exitcode = self.class.gpgcli("#{import_filter_arg} #{gpg_extra_arg} --import") do |stdin, stdout, stderr| # Wrap this into a block because gpg breaks the pipe if it encounters invalid data. begin stdin.print input rescue Errno::EPIPE end stdin.close stdout.readlines end if exitcode > 0 RuntimeError.new(gpgerr.join("\n")) else translate_import_data(gpgout) end end def translate_import_data(gpgoutput) result = {} gpgoutput.grep(/IMPORT_OK/) do |import_ok| next if import_ok.blank? import_status, fingerprint = import_ok.split(/\s/).slice(2, 2) import_status = import_status.to_i states = [] if import_status == 0 states << I18n.t('import_states.unchanged') else IMPORT_FLAGS.each do |text, int| if (import_status & int) > 0 states << I18n.t("import_states.#{text}") end end end result[fingerprint] = states end result end # Unfortunately we can't distinguish between a failure to connect the # keyserver, and a failure to find the key on the server. So we try to # filter misleading errors to check if there are any to be reported. def refresh_key_filter_messages(strings) strings.reject do |line| line.chomp == 'gpg: keyserver refresh failed: No data' || line.match(/^gpgkeys: key .* not found on keyserver/) || line.match(/^gpg: refreshing /) || line.match(/^gpg: requesting key /) || line.match(/^gpg: no valid OpenPGP data found/) end end def self.gpgcli(args) exitcode = -1 errors = [] output = [] base_cmd = gpg_engine.file_name base_args = '--no-greeting --quiet --armor --trust-model always --no-tty --command-fd 0 --status-fd 1' cmd = [base_cmd, base_args, args].flatten.join(' ') Open3.popen3(cmd) do |stdin, stdout, stderr, thread| if block_given? output = yield(stdin, stdout, stderr) else output = stdout.readlines end stdin.close if ! stdin.closed? errors = stderr.readlines exitcode = thread.value.exitstatus end # Don't treat warnings as errors but log them. errors = errors.map do |line| if line.match?(/gpg: WARNING: (unsafe permissions on homedir|using insecure memory)/i) Schleuder.logger.warn(line) nil else line end end.compact [errors, output, exitcode] rescue Errno::ENOENT raise 'Need gpg in $PATH or in $GPGBIN' end def import_filter_arg %{ --import-filter drop-sig='sig_created_d > 0000-00-00'} end end end schleuder-5.0.1/lib/schleuder/gpgme/import_status.rb000066400000000000000000000017601502127241700225650ustar00rootroot00000000000000module GPGME class ImportStatus attr_reader :action # Unfortunately in initialize() @status and @result are not yet initialized. def set_action @action ||= if self.result > 0 # An error happened. # TODO: Give details by going through the list of errors in # "gpg-errors.h" and find out which is present here. 'error' else # TODO: refactor with Ctx#translate_import_data case self.status when 0 'unchanged' when IMPORT_NEW 'imported' else 'updated' end end self end # Force encoding, some databases save "ASCII-8BIT" as binary data. alias_method :orig_fingerprint, :fingerprint def fingerprint orig_fingerprint.encode(Encoding::US_ASCII) end end end schleuder-5.0.1/lib/schleuder/gpgme/key.rb000066400000000000000000000042131502127241700204340ustar00rootroot00000000000000module GPGME class Key # Overwrite to specify the full fingerprint instead of the short key-ID. def to_s primary_subkey = subkeys[0] s = sprintf("%s %4d%s/%s %s\n", primary_subkey.secret? ? 'sec' : 'pub', primary_subkey.length, primary_subkey.pubkey_algo_letter, primary_subkey.fingerprint, primary_subkey.timestamp.strftime('%Y-%m-%d')) uids.each do |user_id| s << "uid\t\t#{user_id.name} <#{user_id.email}>\n" end subkeys.each do |subkey| s << subkey.to_s end s end def generated_at primary_subkey.timestamp end def expired? expired.present? end def summary @summary ||= begin datefmt = '%Y-%m-%d' attribs = [ "0x#{fingerprint}", email, generated_at.strftime(datefmt) ] if usability_issue.present? case usability_issue when :expired attribs << "[expired: #{expires.strftime(datefmt)}]" when :revoked # TODO: add revocation date when it's available. attribs << '[revoked]' else attribs << "[#{usability_issue}]" end end if expires? && ! expired? attribs << "[expires: #{expires.strftime(datefmt)}]" end attribs.join(' ') end end def armored "#{self.to_s}\n\n#{export(armor: true).read}" end def minimal export(minimal: true).to_s end # Force encoding, some databases save "ASCII-8BIT" as binary data. alias_method :orig_fingerprint, :fingerprint def fingerprint orig_fingerprint.encode(Encoding::US_ASCII) end def usable? usability_issue.blank? end def usability_issue if trust.present? trust elsif ! usable_for?(:encrypt) 'not capable of encryption' else nil end end def self.valid_fingerprint?(fp) fp.present? && fp.match?(Schleuder::Conf::FINGERPRINT_REGEXP) end end end schleuder-5.0.1/lib/schleuder/gpgme/key_extractor.rb000066400000000000000000000023141502127241700225270ustar00rootroot00000000000000module GPGME class KeyExtractor # This takes key material and returns those keys from it, that have a UID # matching the given email address, stripped by all other UIDs. def self.extract_by_email_address(email_address, keydata) orig_gnupghome = ENV['GNUPGHOME'] ENV['GNUPGHOME'] = Dir.mktmpdir gpg = GPGME::Ctx.new(armor: true) gpg_arg = %{ --import-filter keep-uid='mbox = #{email_address}'} gpg.import_filtered(keydata, gpg_arg) # Return the fingerprint and the exported, filtered keydata, because # passing the key objects around led to strange problems with some keys, # which produced only a blank string as return value of export(). result = {} gpg.keys.each do |tmp_key| # Skip this key if it has # * no UID – because none survived the import-filter, # * more than one UID – which means the import-filtering failed or # something else went wrong during import. if tmp_key.uids.size == 1 result[tmp_key.fingerprint] = tmp_key.armored end end result ensure FileUtils.rm_rf(ENV['GNUPGHOME'], secure: true) ENV['GNUPGHOME'] = orig_gnupghome end end end schleuder-5.0.1/lib/schleuder/gpgme/sub_key.rb000066400000000000000000000005241502127241700213060ustar00rootroot00000000000000module GPGME class SubKey # Overwrite to specify the full fingerprint instead of the short key-ID. def to_s sprintf("%s %4d%s/%s %s\n", secret? ? 'ssc' : 'sub', length, pubkey_algo_letter, fingerprint, timestamp.strftime('%Y-%m-%d')) end end end schleuder-5.0.1/lib/schleuder/gpgme/user_id.rb000066400000000000000000000006501502127241700212770ustar00rootroot00000000000000module GPGME class UserID def name sanitize_encoding(@name) end def comment sanitize_encoding(@comment) end def uid sanitize_encoding(@uid) end private def sanitize_encoding(str) if str.is_a?(String) && str.encoding != 'UTF-8' str.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '') else str end end end end schleuder-5.0.1/lib/schleuder/http.rb000066400000000000000000000024651502127241700175330ustar00rootroot00000000000000module Schleuder class NetworkError < StandardError; end class NotFoundError < StandardError; end class Http attr_reader :request, :response def initialize(url, options={}) @request = Typhoeus::Request.new(url, default_options.merge(options)) end def run @response = @request.run if @response.success? @response.body elsif @response.timed_out? raise_network_error(response, 'HTTP Request timed out.') elsif @response.code == 404 NotFoundError.new elsif @response.code == 0 # This happens e.g. if no response could be received. raise_network_error(@response, 'No HTTP response received.') else RuntimeError.new(@response.body.to_s.presence || @response.return_message) end end def self.get(url) nth_attempt ||= 1 new(url).run rescue NetworkError => error nth_attempt += 1 if nth_attempt < 4 retry else return error end end private def raise_network_error(response, fallback_msg) raise NetworkError.new( response.body.to_s.presence || response.return_message || fallback_msg ) end def default_options { followlocation: true, proxy: Conf.http_proxy.presence } end end end schleuder-5.0.1/lib/schleuder/key_fetcher.rb000066400000000000000000000047001502127241700210360ustar00rootroot00000000000000module Schleuder class KeyFetcher def initialize(list) @list = list end def fetch(input, locale_key='key_fetched') result = case input when /^http/ fetch_key_by_url(input) when Conf::EMAIL_REGEXP fetch_key_from_keyserver('email', input) when Conf::FINGERPRINT_REGEXP fetch_key_from_keyserver('fingerprint', input) else return I18n.t('key_fetcher.invalid_input') end interpret_fetch_result(result, locale_key) end def fetch_key_by_url(url) case result = Schleuder::Http.get(url) when NotFoundError NotFoundError.new(I18n.t('key_fetcher.url_not_found', url: url)) else result end end def fetch_key_from_keyserver(type, input) if Conf.vks_keyserver.present? result = Schleuder::VksClient.get(type, input) end if (result.blank? || ! result.is_a?(String)) && Conf.sks_keyserver.present? result = Schleuder::SksClient.get(input) end case result when nil RuntimeError.new('No keyserver configured, cannot query anything') when NotFoundError NotFoundError.new(I18n.t('key_fetcher.not_found', input: input)) else result end end private def interpret_fetch_result(result, locale_key) case result when '' I18n.t('key_fetcher.general_error', error: 'Empty response from server') when String import(result, locale_key) when NotFoundError result.to_s when StandardError I18n.t('key_fetcher.general_error', error: result) else raise_unexpected_error(result) end end def import(input, locale_key) result = @list.gpg.import_filtered(input) case result when StandardError I18n.t('key_fetcher.import_error', error: result) when Hash translate_output(locale_key, result).join("\n") else raise_unexpected_error(result) end end def translate_output(locale_key, import_states) import_states.map do |fingerprint, states| key = @list.gpg.find_distinct_key(fingerprint) I18n.t(locale_key, key_summary: key.summary, states: states.to_sentence) end end def raise_unexpected_error(thing) raise "Unexpected output => #{thing.inspect}" end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/000077500000000000000000000000001502127241700215645ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/keyword_handlers/attach_list_key.rb000066400000000000000000000010411502127241700252540ustar00rootroot00000000000000module Schleuder module KeywordHandlers class AttachListKey < Base handles_list_keyword 'attach-listkey', with_method: :attach_list_key def attach_list_key new_part = Mail::Part.new new_part.body = @list.export_key new_part.content_type = 'application/pgp-keys' new_part.content_description = "OpenPGP public key of #{@list.email}" new_part.content_disposition = "attachment; filename=#{@list.fingerprint}.pgpkey" @mail.add_part new_part nil end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/base.rb000066400000000000000000000016211502127241700230230ustar00rootroot00000000000000module Schleuder module KeywordHandlers class Base class << self def handles_request_keyword(keyword, with_method:, has_aliases: []) KeywordHandlersRunner.register_keyword( type: :request, keyword: keyword, handler_class: self, handler_method: with_method, aliases: has_aliases ) end def handles_list_keyword(keyword, with_method:, has_aliases: []) KeywordHandlersRunner.register_keyword( type: :list, keyword: keyword, handler_class: self, handler_method: with_method, aliases: has_aliases ) end end attr_reader :arguments attr_reader :mail def initialize(mail:, arguments:) @arguments = arguments @mail = mail @list = mail.list end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/get_version.rb000066400000000000000000000003331502127241700244340ustar00rootroot00000000000000module Schleuder module KeywordHandlers class GetVersion < Base handles_request_keyword 'get-version', with_method: :get_version def get_version Schleuder::VERSION end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/key_management.rb000066400000000000000000000105321502127241700250760ustar00rootroot00000000000000module Schleuder module KeywordHandlers class KeyManagement < Base handles_request_keyword 'add-key', with_method: 'add_key' handles_request_keyword 'delete-key', with_method: 'delete_key' handles_request_keyword 'list-keys', with_method: 'list_keys' handles_request_keyword 'get-key', with_method: 'get_key' handles_request_keyword 'fetch-key', with_method: 'fetch_key' def add_key results = if @mail.has_attachments? import_keys_from_attachments elsif @mail.first_plaintext_part.body.to_s.present? import_key_from_body else @list.logger.debug 'Found no attachments and an empty body - sending error message' return I18n.t('keyword_handlers.key_management.no_content_found') end import_stati = results.compact.collect(&:imports).flatten if import_stati.blank? return I18n.t('keyword_handlers.key_management.no_imports') end out = [] import_stati.each do |import_status| if import_status.action == 'error' out << I18n.t('keyword_handlers.key_management.key_import_status.error', fingerprint: import_status.fingerprint) else key = @list.gpg.find_distinct_key(import_status.fingerprint) if key out << I18n.t("keyword_handlers.key_management.key_import_status.#{import_status.action}", key_summary: key.summary) end end end out.join("\n\n") end def delete_key if @arguments.blank? return I18n.t( 'keyword_handlers.key_management.delete_key_requires_arguments' ) end @arguments.map do |argument| # Force GPG to match only fingerprints. if argument[0..1] == '0x' fingerprint = argument else fingerprint = "0x#{argument}" end keys = @list.keys(fingerprint) case keys.size when 0 I18n.t('keyword_handlers.key_management.key_not_found', fingerprint: argument) when 1 begin keys.first.delete! I18n.t('keyword_handlers.key_management.deleted', key_string: keys.first.summary) rescue GPGME::Error::Conflict I18n.t('keyword_handlers.key_management.not_deletable', key_string: keys.first.summary) end else # Shouldn't happen, but who knows. I18n.t('errors.too_many_matching_keys', input: argument, key_strings: keys.map(&:to_s).join("\n") ) end end.join("\n\n") end def list_keys args = Array(@arguments.presence || '') args.map do |argument| # In this case it shall be allowed to match keys by arbitrary # sub-strings, therefore we use `list.gpg` directly to not have the # input filtered. @list.gpg.keys(argument).map do |key| key.to_s end end.join("\n\n") end def get_key @arguments.map do |argument| keys = @list.keys(argument) if keys.blank? I18n.t('errors.no_match_for', input: argument) else result = [I18n.t('keyword_handlers.key_management.matching_keys_intro', input: argument)] keys.each do |key| atchm = Mail::Part.new atchm.body = key.armored atchm.content_type = 'application/pgp-keys' atchm.content_disposition = "attachment; filename=#{key.fingerprint}.asc" result << atchm end result.flatten end end end def fetch_key if @arguments.blank? return I18n.t( 'keyword_handlers.key_management.fetch_key_requires_arguments' ) end @arguments.map do |argument| @list.fetch_keys(argument) end end private def import_keys_from_attachments @mail.attachments.map do |attachment| import_from_string(attachment.body.decoded) end end def import_key_from_body [import_from_string(@mail.first_plaintext_part.body.to_s)] end def import_from_string(string) @list.import_key(string) end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/list_management.rb000066400000000000000000000012151502127241700252570ustar00rootroot00000000000000module Schleuder module KeywordHandlers class ListManagement < Base handles_request_keyword 'get-logfile', with_method: 'get_logfile' def get_logfile if File.readable?(@list.logfile) attachment = Mail::Part.new attachment.body = File.read(@list.logfile) attachment.content_disposition = "inline; filename=#{@list.email}.log" intro = I18n.t('keyword_handlers.list_management.logfile_attached', listname: @list.email) [intro, attachment] else I18n.t('keyword_handlers.list_management.no_logfile', listname: @list.email) end end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/resend.rb000066400000000000000000000140711502127241700233740ustar00rootroot00000000000000module Schleuder module KeywordHandlers class Resend < Base handles_list_keyword 'resend-encrypted-only', with_method: :resend_encrypted_only, has_aliases: 'resend-enc' handles_list_keyword 'resend', with_method: :resend handles_list_keyword 'resend-cc', with_method: :resend_cc handles_list_keyword 'resend-cc-encrypted-only', with_method: :resend_cc_encrypted_only, has_aliases: 'resend-cc-enc' handles_list_keyword 'resend-unencrypted', with_method: :resend_unencrypted handles_list_keyword 'resend-cc-unencrypted', with_method: :resend_cc_unencrypted def resend resend_it end def resend_encrypted_only resend_it(encrypted_only: true) end def resend_cc resend_it_cc end def resend_cc_encrypted_only resend_it_cc(encrypted_only: true) end def resend_unencrypted do_resend_unencrypted(:to) end def resend_cc_unencrypted do_resend_unencrypted(:cc) end private def do_resend_unencrypted(target) if ! resend_recipients_valid? return false end recip_map = Hash[Array(@arguments).map { |email| [email, ''] } ] if do_resend(recip_map, target, false) mail.add_subject_prefix_out! end end def resend_it_cc(encrypted_only: false) if ! resend_recipients_valid? return false end recip_map = map_with_keys(encrypted_only: encrypted_only) # Only continue if all recipients are still here. if recip_map.size < @arguments.size recip_map.keys.each do |aborted_sender| @mail.add_pseudoheader(:error, I18n.t('keyword_handlers.resend.aborted', email: aborted_sender)) end return end if do_resend(recip_map, :cc, encrypted_only) @mail.add_subject_prefix_out! end end def resend_it(encrypted_only: false) if ! resend_recipients_valid? return false end recip_map = map_with_keys(encrypted_only: encrypted_only) resent_stati = recip_map.map do |email, key| do_resend({email => key}, :to, encrypted_only) end if resent_stati.include?(true) # At least one message has been resent @mail.add_subject_prefix_out! end end def do_resend(recipients_map, to_or_cc, encrypted_only) if recipients_map.empty? return end gpg_opts = make_gpg_opts(recipients_map, encrypted_only) if gpg_opts == false return false end # Compose and send email new = @mail.clean_copy new[to_or_cc] = recipients_map.keys new.add_public_footer! new.sender = @list.bounce_address # `dup` gpg_opts because `deliver` changes their value and we need them # below to determine encryption! new.gpg gpg_opts.dup if new.deliver add_resent_headers(recipients_map, to_or_cc, gpg_opts[:encrypt]) return true else add_error_header(recipients_map) return false end rescue Net::SMTPFatalError => exc add_error_header(recipients_map) logger.error "Error while sending: #{exc}" return false end def map_with_keys(encrypted_only:) Array(@arguments).inject({}) do |hash, email| keys = @list.keys(email) # Exclude unusable keys. usable_keys = keys.select { |key| key.usable_for?(:encrypt) } case usable_keys.size when 1 hash[email] = usable_keys.first when 0 if encrypted_only # Don't add the email to the result to exclude it from the # recipients. add_resend_msg(email, :error, 'not_resent_no_keys', usable_keys.size, keys.size) else hash[email] = '' end else # Always report this situation, regardless of sending or not. It's # bad and should be fixed. add_resend_msg(email, :notice, 'not_resent_encrypted_no_keys', usable_keys.size, keys.size) if ! encrypted_only hash[email] = '' end end hash end end def make_gpg_opts(recipients_map, encrypted_only) gpg_opts = @list.gpg_sign_options # Do all recipients have a key? if recipients_map.values.map(&:class).uniq == [GPGME::Key] gpg_opts.merge!(encrypt: true) elsif encrypted_only false end gpg_opts end def add_resend_msg(email, severity, msg, usable_keys_size, all_keys_size) @mail.add_pseudoheader(severity, I18n.t("keyword_handlers.resend.#{msg}", email: email, usable_keys: usable_keys_size, all_keys: all_keys_size)) end def add_error_header(recipients_map) @mail.add_pseudoheader(:error, "Resending to #{recipients_map.keys.join(', ')} failed, please check the logs!") end def add_resent_headers(recipients_map, to_or_cc, sent_encrypted) if sent_encrypted prefix = I18n.t('keyword_handlers.resend.encrypted_to') str = "\n" + recipients_map.map do |email, key| "#{email} (#{key.fingerprint})" end.join(",\n") else prefix = I18n.t('keyword_handlers.resend.unencrypted_to') str = ' ' + recipients_map.keys.join(', ') end headername = resent_header_name(to_or_cc) @mail.add_pseudoheader(headername, "#{prefix}#{str}") end def resent_header_name(to_or_cc) if to_or_cc.to_s == 'to' 'resent' else 'resent_cc' end end def resend_recipients_valid? all_valid = true Array(@arguments).each do |address| if ! address.match(Conf::EMAIL_REGEXP) mail.add_pseudoheader(:error, I18n.t('keyword_handlers.resend.invalid_recipient', address: address)) all_valid = false end end all_valid end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/sign_this.rb000066400000000000000000000033531502127241700241040ustar00rootroot00000000000000module Schleuder module KeywordHandlers class SignThis < Base handles_request_keyword 'sign-this', with_method: :sign_this def sign_this if @mail.has_attachments? @list.logger.debug "Signing each attachment's body" intro = I18n.t('keyword_handlers.sign_this.signatures_attached') parts = @mail.attachments.map do |attachment| make_signature_part(attachment) end [intro, parts].flatten elsif @mail.first_plaintext_part.body.to_s.present? @list.logger.debug 'Clear-signing first available text/plain part' clearsign(@mail.first_plaintext_part.body.to_s) else @list.logger.debug 'Found no attachments and an empty body - sending error message' I18n.t('keyword_handlers.sign_this.no_content_found') end end private def make_signature_part(attachment) material = attachment.body.to_s return nil if material.strip.blank? file_basename = attachment.filename.presence || Digest::SHA256.hexdigest(material) @list.logger.debug "Signing #{file_basename}" filename = "#{file_basename}.sig" part = Mail::Part.new part.body = detachsign(material) part.content_type = 'application/pgp-signature' part.content_disposition = "attachment; filename=#{filename}" part.content_description = "OpenPGP signature for '#{file_basename}'" part end def detachsign(thing) crypto.sign(thing, mode: GPGME::SIG_MODE_DETACH).to_s end def clearsign(string) crypto.clearsign(string.to_s).to_s end def crypto @crypto ||= GPGME::Crypto.new(armor: true) end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers/subscription_management.rb000066400000000000000000000170471502127241700270420ustar00rootroot00000000000000module Schleuder module KeywordHandlers class SubscriptionManangement < Base handles_request_keyword 'subscribe', with_method: :subscribe handles_request_keyword 'unsubscribe', with_method: :unsubscribe handles_request_keyword 'list-subscriptions', with_method: :list_subscriptions handles_request_keyword 'set-fingerprint', with_method: :set_fingerprint handles_request_keyword 'unset-fingerprint', with_method: :unset_fingerprint def subscribe if @arguments.blank? return I18n.t( 'keyword_handlers.subscription_management.subscribe_requires_arguments' ) end email = @arguments.shift.to_s.downcase if @arguments.present? # Collect all arguments that look like fingerprint-material fingerprint = '' while @arguments.first.present? && @arguments.first.match(/^(0x)?[a-f0-9]+$/i) fingerprint << @arguments.shift.downcase end # If the collected values aren't a valid fingerprint, then the input # didn't conform with what this code expects, and then the other # values shouldn't be used. unless GPGME::Key.valid_fingerprint?(fingerprint) return I18n.t('keyword_handlers.subscription_management.subscribe_requires_arguments') end if @arguments.present? # Use possibly remaining args as flags. adminflag = @arguments.shift.to_s.downcase.presence unless ['true', 'false'].include?(adminflag) return I18n.t('keyword_handlers.subscription_management.subscribe_requires_arguments') end if @arguments.present? deliveryflag = @arguments.shift.to_s.downcase.presence unless ['true', 'false'].include?(deliveryflag) return I18n.t('keyword_handlers.subscription_management.subscribe_requires_arguments') end end end end sub, _ = @list.subscribe(email, fingerprint, adminflag, deliveryflag) if sub.persisted? I18n.t( 'keyword_handlers.subscription_management.subscribed', email: sub.email, fingerprint: sub.fingerprint, admin: sub.admin, delivery_enabled: sub.delivery_enabled ) else I18n.t( 'keyword_handlers.subscription_management.subscribing_failed', email: sub.email, errors: sub.errors.full_messages.join(".\n") ) end end def unsubscribe # If no address was given we unsubscribe the sender. email = @arguments.first.to_s.downcase.presence || @mail.signer.email # Refuse to unsubscribe the last admin. if @list.admins.size == 1 && @list.admins.first.email == email return I18n.t( 'keyword_handlers.subscription_management.cannot_unsubscribe_last_admin', email: email ) end # TODO: May signers have multiple UIDs? We don't match those currently. if ! @list.from_admin?(@mail) && email != @mail.signer.email # Only admins may unsubscribe others. return I18n.t( 'keyword_handlers.subscription_management.forbidden', email: email ) end sub = @list.subscriptions.where(email: email).first if sub.blank? return I18n.t( 'keyword_handlers.subscription_management.is_not_subscribed', email: email ) end if res = sub.delete I18n.t( 'keyword_handlers.subscription_management.unsubscribed', email: email ) else I18n.t( 'keyword_handlers.subscription_management.unsubscribing_failed', email: email, error: res.errors.to_a ) end end def list_subscriptions subs = if @arguments.blank? @list.subscriptions.all.to_a else @arguments.map do |argument| @list.subscriptions.where('email like ?', "%#{argument}%").to_a end.flatten end if subs.blank? return nil end out = [ I18n.t('keyword_handlers.subscription_management.list_of_subscriptions') ] out << subs.map do |subscription| # Fingerprints are at most 40 characters long, and lines shouldn't # exceed 80 characters if possible. s = subscription.email if subscription.fingerprint.present? s << "\t0x#{subscription.fingerprint}" end if ! subscription.delivery_enabled? s << "\tDelivery disabled!" end s end out.join("\n") end def set_fingerprint if @arguments.blank? return I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_requires_arguments' ) end if @arguments.first.match(/@/) email = @arguments.shift.downcase if email != @mail.signer.email && ! @list.from_admin?(@mail) return I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_only_self' ) end else email = @mail.signer.email end sub = @list.subscriptions.where(email: email).first if sub.blank? return I18n.t( 'keyword_handlers.subscription_management.is_not_subscribed', email: email ) end fingerprint = @arguments.join unless GPGME::Key.valid_fingerprint?(fingerprint) return I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_requires_valid_fingerprint', fingerprint: fingerprint ) end sub.fingerprint = fingerprint if sub.save I18n.t( 'keyword_handlers.subscription_management.fingerprint_set', email: email, fingerprint: sub.fingerprint ) else I18n.t( 'keyword_handlers.subscription_management.setting_fingerprint_failed', email: email, fingerprint: sub.fingerprint, errors: sub.errors.to_a.join("\n") ) end end def unset_fingerprint if @arguments.blank? return I18n.t( 'keyword_handlers.subscription_management.unset_fingerprint_requires_arguments' ) end email = @arguments.shift.to_s.downcase if email != @mail.signer.email && ! @list.from_admin?(mail) return I18n.t( 'keyword_handlers.subscription_management.unset_fingerprint_only_self' ) end if email == @mail.signer.email && @list.from_admin?(@mail) && @arguments.last.to_s.downcase != 'force' return I18n.t( 'keyword_handlers.subscription_management.unset_fingerprint_requires_arguments' ) end sub = @list.subscriptions.where(email: email).first if sub.blank? return I18n.t( 'keyword_handlers.subscription_management.is_not_subscribed', email: email ) end sub.fingerprint = '' if sub.save I18n.t( 'keyword_handlers.subscription_management.fingerprint_unset', email: email ) else I18n.t( 'keyword_handlers.subscription_management.unsetting_fingerprint_failed', email: email, errors: sub.errors.to_a.join("\n") ) end end end end end schleuder-5.0.1/lib/schleuder/keyword_handlers_runner.rb000066400000000000000000000116251502127241700235070ustar00rootroot00000000000000module Schleuder class KeywordHandlersRunner REGISTERED_KEYWORDS = {list: {}, request: {}} RESERVED_KEYWORDS = %w[list-name] class << self attr_reader :keywords def register_keyword(type:, keyword:, handler_class:, handler_method:, aliases:) assert_valid_input!(type: type, keyword: keyword, handler_class: handler_class, handler_method: handler_method) identifiers = [keyword] + Array(aliases) identifiers.each do |identifier| REGISTERED_KEYWORDS[type.to_sym][identifier.to_s.dasherize] = { klass: handler_class, method: handler_method.to_sym } end end def run(type:, list:, mail:) list.logger.debug "Starting #{self}" assert_valid_type!(type) load_additional_keyword_handlers error = check_unknown_keywords(mail, type) return error if error.present? error = check_mandatory_keywords(mail, list) return [error] if error.present? output = mail.keywords.map do |keyword, arguments| if ! is_reserved_keyword?(keyword) run_handler(mail, list, type, keyword.to_s.dasherize, Array(arguments)) end end output.flatten.compact end private def check_unknown_keywords(mail, type) known_keywords = REGISTERED_KEYWORDS[type.to_sym].keys + RESERVED_KEYWORDS given_keywords = mail.keywords.map(&:first) unknown_keywords = given_keywords - known_keywords if unknown_keywords.present? error_messages = unknown_keywords.map do |keyword| I18n.t('errors.unknown_keyword', keyword: keyword) end return error_messages end end def run_handler(mail, list, type, keyword, arguments) list.logger.debug "run_handler() with keyword '#{keyword}'" if list.admin_only?(keyword) && !list.from_admin?(mail) return Schleuder::Errors::KeywordAdminOnly.new(keyword).to_s end keyword_data = REGISTERED_KEYWORDS[type.to_sym][keyword] handler_class = keyword_data[:klass] handler_method = keyword_data[:method] output = handler_class.new(mail: mail, arguments: arguments).send(handler_method) if list.keywords_admin_notify.include?(keyword) notify_admins(type, mail, list, keyword, arguments, output) end return output rescue => exc # Log to system, this information is probably more useful for # system-admins than for list-admins. Schleuder.logger.error(exc.message_with_backtrace) I18n.t('keyword_handlers.handler_failed', keyword: keyword) end def notify_admins(type, mail, list, keyword, arguments, response) msg = I18n.t("keyword_handlers.keyword_admin_notify.#{type}", sender: mail.signer, keyword: keyword, arguments: arguments.join(' '), response: Array(response).join("\n\n") ) list.logger.notify_admin(msg, nil, 'Notice') end def load_additional_keyword_handlers Dir["#{Schleuder::Conf.keyword_handlers_dir}/*.rb"].each do |file| load file end end def check_mandatory_keywords(mail, list) return nil if mail.keywords.blank? listname_keyword = mail.keywords.assoc('list-name') if listname_keyword.blank? return I18n.t(:missing_listname_keyword_error) else listname_args = listname_keyword.last if ! [list.email, list.request_address].include?(listname_args.first) return I18n.t(:wrong_listname_keyword_error) end end end def is_reserved_keyword?(keyword) RESERVED_KEYWORDS.include?(keyword) end def assert_valid_input!(type:, keyword:, handler_class:, handler_method:) assert_valid_type!(type) assert_valid_keyword!(keyword) assert_valid_handler_class!(handler_class) assert_valid_handler_method!(handler_method) end def assert_valid_type!(type) if ! REGISTERED_KEYWORDS.keys.include?(type) raise ArgumentError.new("Argument must be one of #{REGISTERED_KEYWORDS.keys.inspect}, got: #{type.inspect}") end end def assert_valid_keyword!(keyword) if keyword.blank? raise ArgumentError.new("Invalid keyword: #{keyword.inspect}") end end def assert_valid_handler_class!(handler_class) if ! handler_class.is_a?(Class) raise ArgumentError.new("Invalid input for handler_class: #{handler_class.inspect} is not a class") end end def assert_valid_handler_method!(handler_method) if handler_method.blank? raise ArgumentError.new("Invalid input for handler_method: #{handler_method.inspect} is not a valid method name") end end end end end schleuder-5.0.1/lib/schleuder/list.rb000066400000000000000000000272541502127241700175320ustar00rootroot00000000000000module Schleuder class List < ActiveRecord::Base has_many :subscriptions, dependent: :destroy before_destroy :delete_listdirs serialize :headers_to_meta, coder: JSON serialize :bounces_drop_on_headers, coder: JSON serialize :keywords_admin_only, coder: JSON serialize :keywords_admin_notify, coder: JSON validates :email, presence: true, uniqueness: true, email: true validates :fingerprint, presence: true, fingerprint: true validates :send_encrypted_only, :receive_encrypted_only, :receive_signed_only, :receive_authenticated_only, :receive_from_subscribed_emailaddresses_only, :receive_admin_only, :keep_msgid, :bounces_drop_all, :deliver_selfsent, :bounces_notify_admins, :include_list_headers, :include_openpgp_header, :forward_all_incoming_to_admins, :key_auto_import_from_email, boolean: true validates_each :headers_to_meta, :keywords_admin_only, :keywords_admin_notify do |record, attrib, value| value.each do |word| if word !~ /\A[a-z_-]+\z/i record.errors.add(attrib, I18n.t('errors.invalid_characters')) end end end validates_each :bounces_drop_on_headers do |record, attrib, value| value.each do |key, val| if key.to_s !~ /\A[a-z-]+\z/i || val.to_s !~ /\A[[:graph:]]+\z/i record.errors.add(attrib, I18n.t('errors.invalid_characters')) end end end validates :subject_prefix, :subject_prefix_in, :subject_prefix_out, no_line_breaks: true validates :openpgp_header_preference, presence: true, inclusion: { in: %w(sign encrypt signencrypt unprotected none), } validates :max_message_size_kb, :logfiles_to_keep, greater_than_zero: true validates :log_level, presence: true, inclusion: { in: %w(debug info warn error), } validates :language, presence: true, inclusion: { # TODO: find out why we break translations and available_locales if we use I18n.available_locales here. in: %w(de en), } validates :public_footer, :internal_footer, allow_blank: true, format: { with: /\A[[:graph:]\s]*\z/i, } # Some users find it quite confusing when they click "reply-to" and the mail client # doesn't reply to the sender of the mail but the whole mailing list. For those lists it can be # considered to set this value to true. The recipients will then receive e-mails # where the "reply-to" header will contain the reply-to address # of the sender and thus reply to the sender when clicking "reply-to" in a client. # If no "reply-to" is set, the "from"-header of the original sender will be used. # The default is off. validates :set_reply_to_to_sender, boolean: true # Some users find it confusing when the "from" does not contain the original sender # but the list address. For those lists it can be considered to set the munged header. # This will result in a "from"-header like this: "originalsender@original.com via list@list.com" # The default is off. validates :munge_from, boolean: true default_scope { order(:email) } def self.configurable_attributes @configurable_attributes ||= begin all = self.validators.map(&:attributes).flatten.uniq.compact.sort all - [:email, :fingerprint] end end def logfile @logfile ||= File.join(Conf.listlogs_dir, self.email.split('@').reverse, 'list.log') end def logger @logger ||= Listlogger.new(self) end def to_s email end def admins subscriptions.where(admin: true) end def key(fingerprint=self.fingerprint) keys(fingerprint).first end def secret_key keys(self.fingerprint, true).first end def keys(identifier=nil, secret_only=nil) gpg.find_keys(identifier, secret_only) end def import_key(importable) gpg.keyimport(importable) end def import_key_and_find_fingerprint(key_material) return nil if key_material.blank? import_result = import_key(key_material) gpg.interpret_import_result(import_result) end def delete_key(fingerprint) if key = keys(fingerprint).first key.delete! true else false end end def export_key(fingerprint=self.fingerprint) key = keys(fingerprint).first if key.blank? return false end key.armored end def key_minimal_base64_encoded(fingerprint=self.fingerprint) key = keys(fingerprint).first if key.blank? return false end Base64.strict_encode64(key.minimal) end def check_keys now = Time.now checkdate = now + (60 * 60 * 24 * 14) # two weeks unusable = [] expiring = [] keys.each do |key| expiry = key.subkeys.first.expires if expiry && expiry > now && expiry < checkdate # key expires in the near future expdays = ((expiry - now)/86400).to_i expiring << [key, expdays] end if ! key.usable? unusable << [key, key.usability_issue] end end text = '' expiring.each do |key, days| text << I18n.t('key_expires', days: days, key_summary: key.summary ) text << "\n" end unusable.each do |key, usability_issue| text << I18n.t('key_unusable', usability_issue: usability_issue, key_summary: key.summary ) text << "\n" end text end def refresh_keys # reorder keys so the update pattern is random output = self.keys.shuffle.map do |key| # Sleep a short while to make traffic analysis less easy. sleep rand(1.0..5.0) key_fetcher.fetch(key.fingerprint, 'key_updated').presence end # Filter out some "noise" (if a key was unchanged, it wasn't really updated, was it?) # It would be nice to prevent these "false" lines in the first place, but I don't know how. output.reject! do |line| line.match('updated \(unchanged\)') end output.compact.join("\n") end def fetch_keys(input) key_fetcher.fetch(input) end def key_fetcher @key_fetcher ||= KeyFetcher.new(self) end def self.by_recipient(recipient) listname = recipient.gsub(/-(sendkey|request|owner|bounce)@/, '@') where(email: listname).first end def sendkey_address @sendkey_address ||= email.gsub('@', '-sendkey@') end def request_address @request_address ||= email.gsub('@', '-request@') end def owner_address @owner_address ||= email.gsub('@', '-owner@') end def bounce_address @bounce_address ||= email.gsub('@', '-bounce@') end def gpg @gpg_ctx ||= begin # TODO: figure out why set it again... # Set GNUPGHOME when list is created. set_gnupg_home GPGME::Ctx.new armor: true end end # TODO: place this somewhere sensible. # Call cleanup when script finishes. #Signal.trap(0, proc { @list.cleanup }) def cleanup if @gpg_agent_pid Process.kill('TERM', @gpg_agent_pid.to_i) end rescue => e $stderr.puts "Failed to kill gpg-agent: #{e}" end def gpg_sign_options {sign: true, sign_as: self.fingerprint} end def fingerprint=(arg) if arg write_attribute(:fingerprint, arg.gsub(/\s*/, '').gsub(/^0x/, '').chomp.upcase) end end def self.listdir(listname) File.join( Conf.lists_dir, listname.split('@').reverse ) end def listdir @listdir ||= self.class.listdir(self.email) end # A convenience-method to simplify other code. def subscribe(email, fingerprint=nil, adminflag=nil, deliveryflag=nil, key_material=nil) messages = nil args = { list_id: self.id, email: email } if key_material.present? fingerprint, messages = import_key_and_find_fingerprint(key_material) end args[:fingerprint] = fingerprint # ActiveRecord does not treat nil as falsy for boolean columns, so we # have to avoid that in order to not receive an invalid object. The # database will use the column's default-value if no value is being # given. (I'd rather not duplicate the defaults here.) if ! adminflag.nil? args[:admin] = adminflag end if ! deliveryflag.nil? args[:delivery_enabled] = deliveryflag end subscription = Subscription.create(args) [subscription, messages] end def unsubscribe(email, delete_key=false) sub = subscriptions.where(email: email).first if sub.blank? false end if ! sub.destroy return sub end if delete_key sub.delete_key end end def keywords_admin_notify Array(read_attribute(:keywords_admin_notify)) end def keywords_admin_only Array(read_attribute(:keywords_admin_only)) end def admin_only?(keyword) keywords_admin_only.include?(keyword) end def from_admin?(mail) return false if ! mail.was_validly_signed? admins.find do |admin| admin.fingerprint == mail.signing_key.fingerprint end.presence || false end def set_attribute(attrib, value) self.send("#{attrib}=", value) end def send_list_key_to_subscriptions mail = Mail.new mail.from = self.email mail.subject = I18n.t('list_public_key_subject') mail.body = I18n.t('list_public_key_attached') mail.attach_list_key!(self) send_to_subscriptions(mail) true end def send_to_subscriptions(mail, incoming_mail=nil) logger.debug 'Sending to subscriptions.' mail.add_internal_footer! self.subscriptions.each do |subscription| begin if ! subscription.delivery_enabled logger.info "Not sending to #{subscription.email}: delivery is disabled." next end if ! self.deliver_selfsent && incoming_mail&.was_validly_signed? && ( subscription == incoming_mail&.signer ) logger.info "Not sending to #{subscription.email}: delivery of self sent is disabled." next end subscription.send_mail(mail, incoming_mail) rescue => exc msg = I18n.t('errors.delivery_error', email: subscription.email, error: exc.to_s) logger.error msg logger.error exc end end end private def set_gnupg_home ENV['GNUPGHOME'] = listdir end def delete_listdirs if File.exist?(self.listdir) FileUtils.rm_rf(self.listdir, secure: true) Schleuder.logger.info "Deleted #{self.listdir}" end # If listlogs_dir is different from lists_dir, the logfile still exists # and needs to be deleted, too. logfile_dir = File.dirname(self.logfile) if File.exist?(logfile_dir) FileUtils.rm_rf(logfile_dir, secure: true) Schleuder.logger.info "Deleted #{logfile_dir}" end true rescue => exc # Don't use list-logger here — if the list-dir isn't present we can't log to it! Schleuder.logger.error "Error while deleting listdir: #{exc}" return false end end end schleuder-5.0.1/lib/schleuder/list_builder.rb000066400000000000000000000077501502127241700212370ustar00rootroot00000000000000module Schleuder class ListBuilder def initialize(list_attributes, adminemail=nil, adminfingerprint=nil, adminkey=nil) @list_attributes = list_attributes.with_indifferent_access @listname = list_attributes[:email] @fingerprint = list_attributes[:fingerprint] @adminemail = adminemail @adminfingerprint = adminfingerprint @adminkey = adminkey end def read_default_settings hash = YAML.load_file(ENV['SCHLEUDER_LIST_DEFAULTS']) if ! hash.kind_of?(Hash) raise Errors::LoadingListSettingsFailed.new end hash rescue Psych::SyntaxError raise Errors::LoadingListSettingsFailed.new end def run Schleuder.logger.info 'Building new list' if @listname.blank? || ! @listname.match(Conf::EMAIL_REGEXP) return [nil, {'email' => ["'#{@listname}' is not a valid email address"]}] end settings = read_default_settings.merge(@list_attributes) list = List.new(settings) @list_dir = list.listdir create_or_test_dir(@list_dir) # In case listlogs_dir != lists_dir we have to create the basedir of the # list's log-file. create_or_test_dir(File.dirname(list.logfile)) if list.fingerprint.blank? list_key = gpg.keys("<#{list.email}>").first if list_key.nil? list_key = create_key(list) end list.fingerprint = list_key.fingerprint end if ! list.valid? return list end list.save! if @adminemail.blank? msg = nil else sub, msg = list.subscribe(@adminemail, @adminfingerprint, true, true, @adminkey) if sub.errors.present? raise Errors::ActiveModelError.new(sub.errors) end end [list, msg] end def gpg @gpg_ctx ||= begin ENV['GNUPGHOME'] = @list_dir GPGME::Ctx.new end end def create_key(list) Schleuder.logger.info 'Generating key-pair, this could take a while...' gpg.generate_key(key_params(list)) # Get key without knowing the fingerprint yet. keys = list.keys(@listname) if keys.empty? raise Errors::KeyGenerationFailed.new(@list_dir, @listname) elsif keys.size > 1 raise Errors::TooManyKeys.new(@list_dir, @listname) else adduids(list, keys.first) end keys.first end def adduids(list, key) # Add UIDs for -owner and -request. [list.request_address, list.owner_address].each do |address| err = add_uid_to_key(list, address) if err.present? raise err end end # Go through list.key() to re-fetch the key from the keyring, otherwise # we don't see the new UIDs. errors = set_primary_uid_of_key(list) if errors.present? raise errors end rescue => exc raise Errors::KeyAdduidFailed.new(exc.to_s) end def key_params(list) " Key-Type: RSA Key-Length: 4096 Key-Usage: sign Subkey-Type: RSA Subkey-Length: 4096 Subkey-Usage: encrypt Name-Real: #{list.email} Name-Email: #{list.email} Expire-Date: 0 %no-protection " end def create_or_test_dir(dir) if File.exist?(dir) if ! File.directory?(dir) raise Errors::ListdirProblem.new(dir, :not_a_directory) end if ! File.writable?(dir) raise Errors::ListdirProblem.new(dir, :not_writable) end else FileUtils.mkdir_p(dir) end end def set_primary_uid_of_key(list) errors, _ = GPGME::Ctx.gpgcli("--quick-set-primary-uid #{list.email} '#{list.email} <#{list.email}>'") errors.join end def add_uid_to_key(list, email) # Specifying the key via fingerprint apparently doesn't work. errors, _ = GPGME::Ctx.gpgcli("--quick-adduid #{list.email} '#{list.email} <#{email}>'") errors.join end end end schleuder-5.0.1/lib/schleuder/listlogger.rb000066400000000000000000000017261502127241700207260ustar00rootroot00000000000000module Schleuder class Listlogger < ::Logger include LoggerNotifications def initialize(list) super(list.logfile, 'daily') @from = list.email @list = list @adminaddresses = list.admins.map { |sub| [sub.email, sub.key] } @level = ::Logger.const_get(list.log_level.upcase) remove_old_logfiles(list) end # Logger rotates but doesn't delete older files, so we're helping # ourselves. def remove_old_logfiles(list) logfiles_to_keep = list.logfiles_to_keep.to_i if logfiles_to_keep < 1 logfiles_to_keep = list.class.column_defaults['logfiles_to_keep'] end suffix_now = Time.now.strftime('%Y%m%d').to_i del_older_than = suffix_now - logfiles_to_keep Pathname.glob("#{list.logfile}.????????").each do |file| if file.basename.to_s.match(/\.([0-9]{8})$/) if del_older_than.to_i >= $1.to_i file.unlink end end end end end end schleuder-5.0.1/lib/schleuder/logger.rb000066400000000000000000000010011502127241700200140ustar00rootroot00000000000000module Schleuder def logger @logger ||= Logger.new end module_function :logger class Logger < Syslog::Logger include LoggerNotifications def initialize super('Schleuder', Syslog::LOG_MAIL) # We need some sender-address different from the superadmin-address. @from = "#{Etc.getlogin}@#{Addrinfo.getaddrinfo(Socket.gethostname, nil).first.getnameinfo.first}" @adminaddresses = Conf.superadmin @level = ::Logger.const_get(Conf.log_level.upcase) end end end schleuder-5.0.1/lib/schleuder/logger_notifications.rb000066400000000000000000000041361502127241700227610ustar00rootroot00000000000000module Schleuder module LoggerNotifications def adminaddresses @adminaddresses.presence || superadmin end def superadmin Conf.superadmin.presence end def error(string) super(string) notify_admin(string) end def fatal(string, original_message=nil) super(string.to_s + append_original_message(original_message)) notify_admin(string, original_message) end def notify_superadmin(message:, original_message: nil, subject: 'Error') notify_admin(message, original_message, subject, superadmin) end def notify_admin(thing, original_message=nil, subject='Error', recipients=nil) # Minimize using other classes here, we don't know what caused the error. msg_parts = convert_to_msg_parts(thing, original_message) recipients ||= adminaddresses Array(adminaddresses).each do |address, key| mail = Mail.new mail.from = @from mail.to = address mail.subject = subject mail[:Errors_To] = superadmin mail.sender = superadmin msg_parts.each do |msg_part| mail.add_part(msg_part) end if @list.present? gpg_opts = @list.gpg_sign_options if key.present? && key.usable? gpg_opts.merge!(encrypt: true, keys: { address => key.fingerprint }) end mail.gpg gpg_opts mail.header['List-Id'] = "<#{@list.email.gsub('@', '.')}>" end mail.deliver end true end private def convert_to_msg_parts(thing, original_message) msg_parts = Mail::Message.all_to_message_part(thing) if original_message.present? orig_part = Mail::Part.new orig_part.content_type = 'message/rfc822' orig_part.content_description = 'The originally incoming message' orig_part.body = original_message.to_s msg_parts << orig_part end msg_parts end def append_original_message(original_message) if original_message "\n\nOriginal message:\n\n#{original_message.to_s}" else '' end end end end schleuder-5.0.1/lib/schleuder/mail/000077500000000000000000000000001502127241700171425ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/mail/gpg.rb000066400000000000000000000215701502127241700202510ustar00rootroot00000000000000require 'schleuder/mail/gpg/missing_keys_error' require 'schleuder/mail/gpg/version_part' require 'schleuder/mail/gpg/decrypted_part' require 'schleuder/mail/gpg/encrypted_part' require 'schleuder/mail/gpg/inline_decrypted_message' require 'schleuder/mail/gpg/gpgme_helper' require 'schleuder/mail/gpg/signed_part' require 'schleuder/mail/gpg/mime_signed_message' require 'schleuder/mail/gpg/inline_signed_message' module Mail module Gpg BEGIN_PGP_MESSAGE_MARKER = /^-----BEGIN PGP MESSAGE-----/ BEGIN_PGP_SIGNED_MESSAGE_MARKER = /^-----BEGIN PGP SIGNED MESSAGE-----/ # options are: # :sign: sign message using the sender's private key # :sign_as: sign using this key (give the corresponding email address or key fingerprint) # :password: passphrase for the signing key # :keys: A hash mapping recipient email addresses to public keys or public # key ids. Imports any keys given here that are not already part of the # local keychain before sending the mail. # :always_trust: send encrypted mail to untrusted receivers, true by default def self.encrypt(cleartext_mail, options = {}) construct_mail(cleartext_mail, options) do receivers = [] receivers += cleartext_mail.to if cleartext_mail.to receivers += cleartext_mail.cc if cleartext_mail.cc receivers += cleartext_mail.bcc if cleartext_mail.bcc if options[:sign_as] options[:sign] = true options[:signers] = options.delete(:sign_as) elsif options[:sign] options[:signers] = cleartext_mail.from end add_part VersionPart.new add_part EncryptedPart.new(cleartext_mail, options.merge({recipients: receivers})) content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}" body.preamble = options[:preamble] || 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)' if cleartext_mail.protected_headers_subject subject cleartext_mail.protected_headers_subject end end end def self.sign(cleartext_mail, options = {}) options[:sign_as] ||= cleartext_mail.from construct_mail(cleartext_mail, options) do to_be_signed = SignedPart.build(cleartext_mail) add_part to_be_signed add_part to_be_signed.sign(options) content_type "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=#{boundary}" body.preamble = options[:preamble] || 'This is an OpenPGP/MIME signed message (RFC 4880 and 3156)' end end # options are: # :verify: decrypt and verify def self.decrypt(encrypted_mail, options = {}) if encrypted_mime?(encrypted_mail) decrypt_pgp_mime(encrypted_mail, options) elsif encrypted_inline?(encrypted_mail) decrypt_pgp_inline(encrypted_mail, options) else raise EncodingError.new("Unsupported encryption format '#{encrypted_mail.content_type}'") end end def self.signature_valid?(signed_mail, options = {}) if signed_mime?(signed_mail) signature_valid_pgp_mime?(signed_mail, options) elsif signed_inline?(signed_mail) signature_valid_inline?(signed_mail, options) else raise EncodingError.new("Unsupported signature format '#{signed_mail.content_type}'") end end # true if a mail is encrypted def self.encrypted?(mail) return true if encrypted_mime?(mail) return true if encrypted_inline?(mail) false end # true if a mail is signed. # # throws EncodingError if called on an encrypted mail (so only call this method if encrypted? is false) def self.signed?(mail) return true if signed_mime?(mail) return true if signed_inline?(mail) if encrypted?(mail) raise EncodingError.new('Unable to determine signature on an encrypted mail, use :verify option on decrypt()') end false end def self.construct_mail(cleartext_mail, options, &block) Mail.new do self.perform_deliveries = cleartext_mail.perform_deliveries Mail::Gpg.copy_headers cleartext_mail, self # necessary? if cleartext_mail.message_id header['Message-ID'] = cleartext_mail['Message-ID'].value end instance_eval &block end end # decrypts PGP/MIME (RFC 3156, section 4) encrypted mail def self.decrypt_pgp_mime(encrypted_mail, options) if encrypted_mail.parts.length < 2 raise EncodingError.new("RFC 3156 mandates exactly two body parts, found '#{encrypted_mail.parts.length}'") end if !VersionPart.is_version_part? encrypted_mail.parts[0] raise EncodingError.new("RFC 3156 first part not a valid version part '#{encrypted_mail.parts[0]}'") end decrypted = DecryptedPart.new(encrypted_mail.parts[1], options) Mail.new(decrypted.raw_source) do # headers from the encrypted part (set by the initializer above) take # precedence over those from the outer mail. Mail::Gpg.copy_headers encrypted_mail, self, overwrite: false verify_result decrypted.verify_result if options[:verify] end end # decrypts inline PGP encrypted mail def self.decrypt_pgp_inline(encrypted_mail, options) InlineDecryptedMessage.setup(encrypted_mail, options) end def self.verify(signed_mail, options = {}) if signed_mime?(signed_mail) Mail::Gpg::MimeSignedMessage.setup signed_mail, options elsif signed_inline?(signed_mail) Mail::Gpg::InlineSignedMessage.setup signed_mail, options else signed_mail end end # check signature for PGP/MIME (RFC 3156, section 5) signed mail def self.signature_valid_pgp_mime?(signed_mail, options) # MUST contain exactly two body parts if signed_mail.parts.length != 2 raise EncodingError.new("RFC 3156 mandates exactly two body parts, found '#{signed_mail.parts.length}'") end result, verify_result = SignPart.verify_signature(signed_mail.parts[0], signed_mail.parts[1], options) signed_mail.verify_result = verify_result return result end # check signature for inline signed mail def self.signature_valid_inline?(signed_mail, options) result = nil if signed_mail.multipart? signed_mail.parts.each do |part| if signed_inline?(part) if result.nil? result = true signed_mail.verify_result = [] end result &= signature_valid_inline?(part, options) signed_mail.verify_result << part.verify_result end end else result, verify_result = GpgmeHelper.inline_verify(signed_mail.body.to_s, options) signed_mail.verify_result = verify_result end return result end # copies all header fields from mail in first argument to that given last def self.copy_headers(from, to, overwrite: true) from.header.fields.each do |field| if overwrite || to.header[field.name].nil? to.header[field.name] = field.value end end end # check if PGP/MIME encrypted (RFC 3156) def self.encrypted_mime?(mail) mail.has_content_type? && mail.mime_type == 'multipart/encrypted' && mail.content_type_parameters[:protocol] == 'application/pgp-encrypted' end # check if inline PGP (i.e. if any parts of the mail includes # the PGP MESSAGE marker) def self.encrypted_inline?(mail) begin return true if mail.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue end if mail.multipart? mail.parts.each do |part| begin return true if part.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue end return true if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type && /.*\.(?:pgp|gpg|asc)$/ =~ part.content_type_parameters[:name] && part.content_type_parameters[:name] != 'signature.asc' # that last condition above prevents false positives in case e.g. # someone forwards a mime signed mail including signature. end end false end # check if PGP/MIME signed (RFC 3156) def self.signed_mime?(mail) mail.has_content_type? && mail.mime_type == 'multipart/signed' && mail.content_type_parameters[:protocol] == 'application/pgp-signature' end # check if inline PGP (i.e. if any parts of the mail includes # the PGP SIGNED marker) def self.signed_inline?(mail) begin return true if mail.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue end if mail.multipart? mail.parts.each do |part| begin return true if part.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue end end end false end end end schleuder-5.0.1/lib/schleuder/mail/gpg/000077500000000000000000000000001502127241700177175ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/mail/gpg/decrypted_part.rb000066400000000000000000000011431502127241700232540ustar00rootroot00000000000000require 'schleuder/mail/gpg/verified_part' module Mail module Gpg class DecryptedPart < VerifiedPart # options are: # # :verify: decrypt and verify def initialize(cipher_part, options = {}) if cipher_part.mime_type != EncryptedPart::CONTENT_TYPE raise EncodingError.new("RFC 3156 incorrect mime type for encrypted part '#{cipher_part.mime_type}'") end decrypted = GpgmeHelper.decrypt(cipher_part.body.decoded, options) self.verify_result = decrypted.verify_result if options[:verify] super(decrypted) end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/delivery_handler.rb000066400000000000000000000022131502127241700235620ustar00rootroot00000000000000module Mail module Gpg class DeliveryHandler def self.deliver_mail(mail) if mail.gpg encrypted_mail = nil begin options = mail.gpg.is_a?(TrueClass) ? { encrypt: true } : mail.gpg if options[:encrypt] encrypted_mail = Mail::Gpg.encrypt(mail, options) elsif options[:sign] || options[:sign_as] encrypted_mail = Mail::Gpg.sign(mail, options) else # encrypt and sign are off -> do not encrypt or sign yield end rescue StandardError raise $! if mail.raise_encryption_errors end if encrypted_mail if dm = mail.delivery_method encrypted_mail.instance_variable_set :@delivery_method, dm end encrypted_mail.perform_deliveries = mail.perform_deliveries encrypted_mail.raise_delivery_errors = mail.raise_delivery_errors encrypted_mail.deliver end else yield end rescue StandardError raise $! if mail.raise_delivery_errors end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/encrypted_part.rb000066400000000000000000000031341502127241700232700ustar00rootroot00000000000000module Mail module Gpg class EncryptedPart < Mail::Part CONTENT_TYPE = 'application/octet-stream' # options are: # # :signers : sign using this key (give the corresponding email address) # :password: passphrase for the signing key # :recipients : array of receiver addresses # :keys : A hash mapping recipient email addresses to public keys or public # key ids. Imports any keys given here that are not already part of the # local keychain before sending the mail. If this option is given, strictly # only the key material from this hash is used, ignoring any keys for # recipients that might have been added to the local key chain but are # not mentioned here. # :always_trust : send encrypted mail to untrusted receivers, true by default # :filename : define a custom name for the encrypted file attachment def initialize(cleartext_mail, options = {}) if cleartext_mail.protected_headers_subject cleartext_mail.content_type_parameters['protected-headers'] = 'v1' end options = { always_trust: true }.merge options encrypted = GpgmeHelper.encrypt(cleartext_mail.encoded, options) super() do body encrypted.to_s filename = options[:filename] || 'encrypted.asc' content_type "#{CONTENT_TYPE}; name=\"#{filename}\"" content_disposition "inline; filename=\"#{filename}\"" content_description 'OpenPGP encrypted message' end end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/gpgme_ext.rb000066400000000000000000000003171502127241700222240ustar00rootroot00000000000000require 'schleuder/mail/gpg/verify_result_attribute' # extend GPGME::Data with an attribute to hold the result of signature # verifications class GPGME::Data include Mail::Gpg::VerifyResultAttribute end schleuder-5.0.1/lib/schleuder/mail/gpg/gpgme_helper.rb000066400000000000000000000123711502127241700227060ustar00rootroot00000000000000require 'schleuder/mail/gpg/gpgme_ext' # GPGME methods for encryption/decryption/signing module Mail module Gpg class GpgmeHelper def self.encrypt(plain, options = {}) options = options.merge({armor: true}) plain_data = GPGME::Data.new(plain) cipher_data = GPGME::Data.new(options[:output]) recipient_keys = keys_for_data options[:recipients], options.delete(:keys) if recipient_keys.empty? raise MissingKeysError.new('No keys to encrypt to!') end flags = 0 flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust] GPGME::Ctx.new(options) do |ctx| begin if options[:sign] if options[:signers] && options[:signers].size > 0 signers = GPGME::Key.find(:secret, options[:signers], :sign) ctx.add_signer(*signers) end ctx.encrypt_sign(recipient_keys, plain_data, cipher_data, flags) else ctx.encrypt(recipient_keys, plain_data, cipher_data, flags) end rescue GPGME::Error::UnusablePublicKey => exc exc.keys = ctx.encrypt_result.invalid_recipients raise exc rescue GPGME::Error::UnusableSecretKey => exc exc.keys = ctx.sign_result.invalid_signers raise exc end end cipher_data.seek(0) cipher_data end def self.decrypt(cipher, options = {}) cipher_data = GPGME::Data.new(cipher) plain_data = GPGME::Data.new(options[:output]) GPGME::Ctx.new(options) do |ctx| begin if options[:verify] ctx.decrypt_verify(cipher_data, plain_data) plain_data.verify_result = ctx.verify_result else ctx.decrypt(cipher_data, plain_data) end rescue GPGME::Error::UnsupportedAlgorithm => exc exc.algorithm = ctx.decrypt_result.unsupported_algorithm raise exc rescue GPGME::Error::WrongKeyUsage => exc exc.key_usage = ctx.decrypt_result.wrong_key_usage raise exc end end plain_data.seek(0) plain_data end def self.sign(plain, options = {}) options.merge!({ armor: true, signer: options.delete(:sign_as), mode: GPGME::SIG_MODE_DETACH }) crypto = GPGME::Crypto.new crypto.sign GPGME::Data.new(plain), options end # returns [success(bool), VerifyResult(from gpgme)] # success will be true when there is at least one sig and no invalid sig def self.sign_verify(plain, signature, options = {}) signed_data = GPGME::Data.new(plain) signature = GPGME::Data.new(signature) success = verify_result = nil GPGME::Ctx.new(options) do |ctx| ctx.verify signature, signed_data, nil verify_result = ctx.verify_result signatures = verify_result.signatures success = signatures && signatures.size > 0 && signatures.detect{|s| !s.valid? }.nil? end return [success, verify_result] end def self.inline_verify(signed_text, options = {}) signed_data = GPGME::Data.new(signed_text) success = verify_result = nil GPGME::Ctx.new(options) do |ctx| ctx.verify signed_data, nil verify_result = ctx.verify_result signatures = verify_result.signatures success = signatures && signatures.size > 0 && signatures.detect{|s| !s.valid? }.nil? end return [success, verify_result] end # normalizes the list of recipients' emails, key ids and key data to a # list of Key objects # # if key_data is given, _only_ key material from there is used, # and eventually already imported keys in the keychain are ignored. def self.keys_for_data(emails_or_shas_or_keys, key_data = nil) if key_data # in this case, emails_or_shas_or_keys is supposed to be the list of # recipients, and key_data the key material to be used. # We now map these to whatever we find in key_data for each of these # addresses. [emails_or_shas_or_keys].flatten.map do |r| k = key_data[r] key_id = case k when GPGME::Key # assuming this is already imported k.fingerprint when nil, '' # nothing nil when /-----BEGIN PGP/ # ASCII key data GPGME::Key.import(k).imports.map(&:fpr) else # key id or fingerprint k end unless key_id.nil? || key_id.empty? GPGME::Key.find(:public, key_id, :encrypt) end end.flatten.compact elsif emails_or_shas_or_keys && (emails_or_shas_or_keys.size > 0) # key lookup in keychain for all receivers GPGME::Key.find :public, emails_or_shas_or_keys, :encrypt else # empty array given [] end end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/inline_decrypted_message.rb000066400000000000000000000074671502127241700253070ustar00rootroot00000000000000require 'schleuder/mail/gpg/verified_part' # decryption of the so called 'PGP-Inline' message types # this is not a standard, so the implementation is based on the notes # here http://binblog.info/2008/03/12/know-your-pgp-implementation/ # and on test messages generated with the Mozilla Enigmail OpenPGP # plugin https://www.enigmail.net module Mail module Gpg class InlineDecryptedMessage < Mail::Message # options are: # # :verify: decrypt and verify def self.setup(cipher_mail, options = {}) if cipher_mail.multipart? self.new do Mail::Gpg.copy_headers cipher_mail, self # Drop the HTML-part of a multipart/alternative-message if it is # inline-encrypted: that ciphertext is probably wrapped in HTML, # which GnuPG chokes upon, so we would have to parse the HTML to # handle the message-part properly. # Also it's not clear how to handle the resulting plain-text: is # it HTML or simple text? That depends on the sending MUA and # the original input. # In summary, that's too much complications. if cipher_mail.mime_type == 'multipart/alternative' && cipher_mail.html_part.present? && cipher_mail.html_part.body.decoded.include?('-----BEGIN PGP MESSAGE-----') cipher_mail.parts.delete_if do |part| part[:content_type].content_type == 'text/html' end # Set the content-type of the newly generated message to # something less confusing. content_type 'multipart/mixed' # Leave a marker for other code. header['X-MailGpg-Deleted-Html-Part'] = 'true' end cipher_mail.parts.each do |part| p = VerifiedPart.new do |p| if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type # encrypted attachment, we set the content_type to the generic 'application/octet-stream' # and remove the .pgp/gpg/asc from name/filename in header fields decrypted = GpgmeHelper.decrypt(part.decoded, options) p.verify_result decrypted.verify_result if options[:verify] p.content_type part.content_type.sub(/application\/(?:octet-stream|pgp-encrypted)/, 'application/octet-stream') .sub(/name=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'name="\1"') p.content_disposition part.content_disposition.sub(/filename=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'filename="\1"') p.content_transfer_encoding Mail::Encodings::Base64 p.body Mail::Encodings::Base64.encode(decrypted.to_s) else body = part.body.decoded if body.include?('-----BEGIN PGP MESSAGE-----') decrypted = GpgmeHelper.decrypt(body, options) p.verify_result decrypted.verify_result if options[:verify] p.body decrypted.to_s else p.content_type part.content_type p.content_transfer_encoding part.content_transfer_encoding p.body part.body.to_s end end end add_part p end end else decrypted = cipher_mail.body.empty? ? '' : GpgmeHelper.decrypt(cipher_mail.body.decoded, options) self.new do cipher_mail.header.fields.each do |field| header[field.name] = field.value end body decrypted.to_s verify_result decrypted.verify_result if options[:verify] && decrypted != '' end end end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/inline_signed_message.rb000066400000000000000000000047371502127241700245720ustar00rootroot00000000000000require 'schleuder/mail/gpg/verified_part' module Mail module Gpg class InlineSignedMessage < Mail::Message def self.setup(signed_mail, options = {}) if signed_mail.multipart? self.new do global_verify_result = [] signed_mail.header.fields.each do |field| header[field.name] = field.value end signed_mail.parts.each do |part| if Mail::Gpg.signed_inline?(part) signed_text = part.body.to_s success, vr = GpgmeHelper.inline_verify(signed_text, options) p = VerifiedPart.new(part) if success p.body self.class.strip_inline_signature signed_text end p.verify_result vr global_verify_result << vr add_part p else add_part part end end verify_result global_verify_result end else self.new do signed_mail.header.fields.each do |field| header[field.name] = field.value end signed_text = signed_mail.body.to_s success, vr = GpgmeHelper.inline_verify(signed_text, options) if success body self.class.strip_inline_signature signed_text else body signed_text end verify_result vr end end end END_SIGNED_TEXT = '-----END PGP SIGNED MESSAGE-----' END_SIGNED_TEXT_RE = /^#{END_SIGNED_TEXT}\s*$/ INLINE_SIG_RE = Regexp.new('^-----BEGIN PGP SIGNATURE-----\s*$.*^-----END PGP SIGNATURE-----\s*$', Regexp::MULTILINE) BEGIN_SIG_RE = /^(-----BEGIN PGP SIGNATURE-----)\s*$/ # utility method to remove inline signature and related pgp markers def self.strip_inline_signature(signed_text) if signed_text =~ INLINE_SIG_RE signed_text = signed_text.dup if signed_text !~ END_SIGNED_TEXT_RE # insert the 'end of signed text' marker in case it is missing signed_text = signed_text.gsub BEGIN_SIG_RE, "-----END PGP SIGNED MESSAGE-----\n\\1" end signed_text.gsub! INLINE_SIG_RE, '' signed_text.strip! end # Strip possible inline-"headers" (e.g. "Hash: SHA256", or "Comment: something"). signed_text.gsub(/(.*^-----BEGIN PGP SIGNED MESSAGE-----\n)(.*?)^$(.+)/m, '\1\3') end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/mime_signed_message.rb000066400000000000000000000014111502127241700242250ustar00rootroot00000000000000require 'schleuder/mail/gpg/verified_part' module Mail module Gpg class MimeSignedMessage < Mail::Message def self.setup(signed_mail, options = {}) content_part, signature = signed_mail.parts success, vr = SignPart.verify_signature(content_part, signature, options) self.new do verify_result vr signed_mail.header.fields.each do |field| header[field.name] = field.value end content_part.header.fields.each do |field| header[field.name] = field.value end if content_part.multipart? content_part.parts.each{|part| add_part part} else body content_part.body.raw_source end end end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/missing_keys_error.rb000066400000000000000000000001261502127241700241600ustar00rootroot00000000000000module Mail module Gpg class MissingKeysError < StandardError end end end schleuder-5.0.1/lib/schleuder/mail/gpg/sign_part.rb000066400000000000000000000027501502127241700222360ustar00rootroot00000000000000module Mail module Gpg class SignPart < Mail::Part def initialize(cleartext_mail, options = {}) signature = GpgmeHelper.sign(cleartext_mail.encoded, options) super() do body signature.to_s content_type "application/pgp-signature; name=\"signature.asc\"" content_disposition 'attachment; filename="signature.asc"' content_description 'OpenPGP digital signature' end end # true if all signatures are valid def self.signature_valid?(plain_part, signature_part, options = {}) verify_signature(plain_part, signature_part, options)[0] end # will return [success(boolean), verify_result(as returned by gpgme)] def self.verify_signature(plain_part, signature_part, options = {}) if !(signature_part.has_content_type? && ('application/pgp-signature' == signature_part.mime_type)) return false end # Work around the problem that plain_part.raw_source prefixes an # erroneous CRLF, . if ! plain_part.raw_source.empty? plaintext = [ plain_part.header.raw_source, "\r\n\r\n", plain_part.body.raw_source ].join else plaintext = plain_part.encoded end signature = signature_part.body.encoded GpgmeHelper.sign_verify(plaintext, signature, options) end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/signed_part.rb000066400000000000000000000014121502127241700225410ustar00rootroot00000000000000require 'mail/part' require 'schleuder/mail/gpg/sign_part' module Mail module Gpg class SignedPart < Mail::Part def self.build(cleartext_mail) new do if cleartext_mail.body.multipart? if cleartext_mail.content_type =~ /^(multipart[^;]+)/ # preserve multipart/alternative etc content_type $1 else content_type 'multipart/mixed' end cleartext_mail.body.parts.each do |p| add_part p end else content_type cleartext_mail.content_type body cleartext_mail.body.raw_source end end end def sign(options) SignPart.new(self, options) end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/verified_part.rb000066400000000000000000000002521502127241700230660ustar00rootroot00000000000000require 'schleuder/mail/gpg/verify_result_attribute' module Mail module Gpg class VerifiedPart < Mail::Part include VerifyResultAttribute end end end schleuder-5.0.1/lib/schleuder/mail/gpg/verify_result_attribute.rb000066400000000000000000000013271502127241700252340ustar00rootroot00000000000000module Mail module Gpg module VerifyResultAttribute # the result of signature verification, as provided by GPGME def verify_result(result = nil) if result self.verify_result = result else @verify_result end end def verify_result=(result) @verify_result = result end # checks validity of signatures (true / false) def signature_valid? sigs = self.signatures sigs.any? && sigs.all?{|s| s.valid?} end # list of all signatures from verify_result def signatures [verify_result].flatten.compact.map do |vr| vr.signatures end.flatten.compact end end end end schleuder-5.0.1/lib/schleuder/mail/gpg/version_part.rb000066400000000000000000000010031502127241700227510ustar00rootroot00000000000000require 'mail/part' module Mail module Gpg class VersionPart < Mail::Part VERSION_1 = 'Version: 1' CONTENT_TYPE = 'application/pgp-encrypted' CONTENT_DESC = 'PGP/MIME Versions Identification' def initialize(*args) super body VERSION_1 content_type CONTENT_TYPE content_description CONTENT_DESC end def self.is_version_part?(part) part.mime_type == CONTENT_TYPE && part.body =~ /#{VERSION_1}/ end end end end schleuder-5.0.1/lib/schleuder/mail/message.rb000066400000000000000000000610631502127241700211210ustar00rootroot00000000000000require 'schleuder/mail/gpg/delivery_handler' require 'schleuder/mail/gpg/verify_result_attribute' module Mail # creates a Mail::Message likes schleuder def self.create_message_to_list(msg, recipient, list) mail = Mail.new(msg) mail.list = list mail.recipient = recipient # don't freeze here, as the mail might not be fully # parsed as body is lazy evaluated and might still # be changed later. mail.original_message = mail.dup #.freeze mail end # TODO: Test if subclassing breaks integration of mail-gpg. class Message include Mail::Gpg::VerifyResultAttribute #for Mail::Gpg attr_accessor :recipient attr_accessor :original_message attr_accessor :list attr_accessor :protected_headers_subject attr_writer :dynamic_pseudoheaders attr_accessor :raise_encryption_errors # for Mail::Gpg # TODO: This should be in initialize(), but I couldn't understand the # strange errors about wrong number of arguments when overriding # Message#initialize. def setup if self.encrypted? # Specify 'loopback'-pinentry-mode to ensure that gnupg never-ever # tries to interactively ask for a passphrase. new = self.decrypt(verify: true, pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK) # Test if there's a signed multipart inside the ciphertext # ("encapsulated" format of pgp/mime). if encapsulated_signed?(new) new = new.verify end elsif self.signed? new = self.verify else new = self end new.list = self.list new.recipient = self.recipient new.gpg list.gpg_sign_options new.original_message = self.dup.freeze # Trigger method early to save the information. Later some information # might be gone (e.g. request-keywords that delete subscriptions or # keys). new.signer new.dynamic_pseudoheaders = self.dynamic_pseudoheaders.dup # Store previously protected subject for later access. # mail-gpg pulls headers from the decrypted mime parts "up" into the main # headers, which reveals protected subjects. if self.subject != new.subject new.protected_headers_subject = self.subject.dup end # Delete the protected headers which might leak information. if new.parts.first && new.parts.first.content_type == 'text/rfc822-headers; protected-headers=v1' new.parts.shift end new end def clean_copy(with_pseudoheaders=false) clean = Mail.new clean.list = self.list clean.gpg self.list.gpg_sign_options clean.from = list.email clean.subject = self.subject clean.protected_headers_subject = self.protected_headers_subject clean.add_msgids(list, self) clean.add_list_headers(list) clean.add_openpgp_headers(list) if with_pseudoheaders new_part = Mail::Part.new new_part.body = self.pseudoheaders(list) clean.add_part new_part end if self.protected_headers_subject.present? new_part = Mail::Part.new new_part.content_type = 'text/rfc822-headers; protected-headers=v1' new_part.body = "Subject: #{self.subject}\n" clean.add_part new_part end # Attach body or mime-parts in a new wrapper-part, to preserve the # original mime-structure. # We can't use self.to_s here — that includes all the headers we *don't* # want to copy. wrapper_part = Mail::Part.new # Copy headers to are relevant for the mime-structure. wrapper_part.content_type = self.content_type wrapper_part.content_transfer_encoding = self.content_transfer_encoding if self.content_transfer_encoding wrapper_part.content_disposition = self.content_disposition if self.content_disposition wrapper_part.content_description = self.content_description if self.content_description # Copy contents. if self.multipart? self.parts.each do |part| wrapper_part.add_part(part) end else # We copied the content-headers, so we need to copy the body encoded. # Otherwise the content might become unlegible. wrapper_part.body = self.body.encoded end clean.add_part(wrapper_part) clean end def prepend_part(part) self.add_part(part) self.parts.unshift(parts.delete_at(parts.size-1)) end def add_public_footer! # Add public_footer unless it's empty?. add_footer!(:public_footer) end def add_internal_footer! add_footer!(:internal_footer) end def was_encrypted? Mail::Gpg.encrypted?(original_message) end def signature case signatures.size when 0 if multipart? signature_multipart_inline else nil end when 1 signatures.first else raise 'Multiple signatures found! Cannot handle!' end end def was_validly_signed? signature.present? && signature.valid? && signer.present? end def signer @signer ||= begin if signing_key.present? # Look for a subscription that matches the sending address, in case # there're multiple subscriptions for the same key. As a fallback use # the first subscription found. sender_email = self.from.to_s.downcase subscriptions = list.subscriptions.where(fingerprint: signing_key.fingerprint) subscriptions.where(email: sender_email).first || subscriptions.first end end end # The fingerprint of the signature might be the one of a sub-key, but the # subscription-assigned fingerprints are (should be) the ones of the # primary keys, so we need to look up the key. def signing_key @signing_key ||= begin if signature.present? list.keys(signature.fpr).first end end end def reply_to_signer(output) reply = self.reply self.class.all_to_message_part(output).each do |part| reply.add_part(part) end self.signer.send_mail(reply) end def self.all_to_message_part(input) Array(input).map do |thing| case thing when Mail::Part thing when String, StandardError Mail::Part.new do body thing.to_s end else raise "Don't know how to handle input: #{thing.inspect}" end end end def sendkey_request? @recipient.match(/-sendkey@/) end def to_owner? @recipient.match(/-owner@/) end def request? @recipient.match(/-request@/) end def automated_message? @recipient.match(/-bounce@/).present? || # Empty Return-Path self.return_path.to_s == '<>' || bounced? end def bounced? @bounced ||= bounce_detected? || (error_status != 'unknown') end def error_status @error_status ||= detect_error_code end def keywords return @keywords if @keywords part = first_plaintext_part if part.blank? return [] end @keywords, lines = extract_keywords(part.decoded.lines) new_body = lines.join # Work around problems with re-encoding the body. If we delete the # content-transfer-encoding prior to re-assigning the body, and let Mail # decide itself how to encode, it works. If we don't, some # character-sequences are not properly re-encoded. part.content_transfer_encoding = nil # Set the right charset on the now parsed body part.charset = new_body.encoding.to_s part.body = new_body @keywords end def add_subject_prefix! _add_subject_prefix(nil) end def add_subject_prefix_in! _add_subject_prefix(:in) end def add_subject_prefix_out! _add_subject_prefix(:out) end def add_pseudoheader(string_or_key, value=nil) dynamic_pseudoheaders << make_pseudoheader(string_or_key, value) end def make_pseudoheader(key, value) output = "#{key.to_s.camelize}: #{value.to_s}" # wrap lines after 76 with 2 indents output.gsub(/(.{1,76})( +|$)\n?/, " \\1\n").chomp.lstrip end def dynamic_pseudoheaders @dynamic_pseudoheaders ||= [] end def signature_state # Careful to add information about the incoming signature. GPGME # throws exceptions if it doesn't know the key. if self.signature.present? # Some versions of gpgme return nil if the key is unknown, so we check # for that manually and provide our own fallback. (Calling # `signature.key` results in an EOFError in that case.) if signing_key.present? signature_state = signature.to_s else signature_state = I18n.t('signature_states.unknown', fingerprint: self.signature.fingerprint) end else signature_state = I18n.t('signature_states.unsigned') end signature_state end def encryption_state if was_encrypted? encryption_state = I18n.t('encryption_states.encrypted') else encryption_state = I18n.t('encryption_states.unencrypted') end encryption_state end def standard_pseudoheaders(list) if @standard_pseudoheaders.present? return @standard_pseudoheaders else @standard_pseudoheaders = [] end Array(list.headers_to_meta).each do |field| value = case field.to_s when 'sig' then signature_state when 'enc' then encryption_state else self.header[field.to_s] end @standard_pseudoheaders << make_pseudoheader(field.to_s, value) end @standard_pseudoheaders end def pseudoheaders(list) separator = '------------------------------------------------------------------------------' (standard_pseudoheaders(list) + dynamic_pseudoheaders).flatten.join("\n") + "\n" + separator + "\n" end def add_msgids(list, orig) if list.keep_msgid # Don't use `orig['in-reply-to']` here, because that sometimes fails to # parse the original value and then returns it without the # angle-brackets. self.message_id = clutch_anglebrackets(orig.message_id) self.in_reply_to = clutch_anglebrackets(orig.in_reply_to) self.references = clutch_anglebrackets(orig.references) end end def add_list_headers(list) if list.include_autocrypt_header # Inject whitespaces, to let Mail break the string at these points # leading to correct wrapping. keydata = list.key_minimal_base64_encoded.gsub(/(.{78})/, '\1 ') self['Autocrypt'] = "addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata}" end if list.include_list_headers self['List-Id'] = "<#{list.email.gsub('@', '.')}>" self['List-Owner'] = " (Use list's public key)" self['List-Help'] = '' postmsg = if list.receive_admin_only 'NO (Admins only)' elsif list.receive_authenticated_only " (Subscribers only)" else "" end self['List-Post'] = postmsg end end def add_openpgp_headers(list) if list.include_openpgp_header if list.openpgp_header_preference == 'none' pref = '' else pref = "preference=#{list.openpgp_header_preference}" # TODO: simplify. pref << ' (' if list.receive_admin_only pref << 'Only encrypted and signed emails by list-admins are accepted' elsif ! list.receive_authenticated_only if list.receive_encrypted_only && list.receive_signed_only pref << 'Only encrypted and signed emails are accepted' elsif list.receive_encrypted_only && ! list.receive_signed_only pref << 'Only encrypted emails are accepted' elsif ! list.receive_encrypted_only && list.receive_signed_only pref << 'Only signed emails are accepted' else pref << 'All kind of emails are accepted' end elsif list.receive_authenticated_only if list.receive_encrypted_only pref << 'Only encrypted and signed emails by subscribers are accepted' else pref << 'Only signed emails by subscribers are accepted' end else pref << 'All kind of emails are accepted' end pref << ')' end fingerprint = list.fingerprint comment = "(Send an email to #{list.sendkey_address} to receive the public-key)" self['OpenPGP'] = "id=0x#{fingerprint} #{comment}; #{pref}" end end def empty? if self.multipart? if self.parts.empty? return true else # Test parts recursively. E.g. Thunderbird with activated # memoryhole-headers send nested parts that might still be empty. return parts.inject(true) { |result, part| result && part.empty? } end else return self.body.empty? end end def first_plaintext_part(part=nil) part ||= self if part.multipart? first_plaintext_part(part.parts.first) elsif part.mime_type == 'text/plain' part else nil end end def attach_list_key!(list) filename = "#{list.email}.asc" self.add_file({ filename: filename, content: list.export_key }) self.attachments[filename].content_type = 'application/pgp-keys' self.attachments[filename].content_description = 'OpenPGP public key' true end # turn on gpg encryption / set gpg options. # # options are: # # encrypt: encrypt the message. defaults to true # sign: also sign the message. false by default # sign_as: UIDs to sign the message with # # See Mail::Gpg methods encrypt and sign for more # possible options # # mail.gpg encrypt: true # mail.gpg encrypt: true, sign: true # mail.gpg encrypt: true, sign_as: "other_address@host.com" # # sign-only mode is also supported: # mail.gpg sign: true # mail.gpg sign_as: 'jane@doe.com' # # To turn off gpg encryption use: # mail.gpg false # def gpg(options = nil) case options when nil @gpg when false @gpg = nil if Mail::Gpg::DeliveryHandler == delivery_handler self.delivery_handler = nil end nil else self.raise_encryption_errors = true if raise_encryption_errors.nil? @gpg = options self.delivery_handler ||= Mail::Gpg::DeliveryHandler nil end end # true if this mail is encrypted def encrypted? Mail::Gpg.encrypted?(self) end # returns the decrypted mail object. # # pass verify: true to verify signatures as well. The gpgme verification # result will be available via decrypted_mail.verify_result def decrypt(options = {}) Mail::Gpg.decrypt(self, options) end # true if this mail is signed (but not encrypted) def signed? Mail::Gpg.signed?(self) end # verify signatures. returns a new mail object with signatures removed and # populated verify_result. # # verified = signed_mail.verify() # verified.signature_valid? # signers = mail.signatures.map{|sig| sig.from} # # use import_missing_keys: true in order to try to fetch and import # unknown keys for signature validation def verify(options = {}) Mail::Gpg.verify(self, options) end def repeat_validation! new = self.original_message.dup.setup self.verify_result = new.verify_result @signatures = new.signatures dynamic_pseudoheaders << new.dynamic_pseudoheaders end private def extract_keywords(content_lines) keywords = [] in_keyword_block = false content_lines.each_with_index do |line, i| if line.blank? # Swallow the line: before the actual content or keywords block begins we want to drop blank lines. content_lines[i] = nil # Stop interpreting the following line as argument to the previous keyword. in_keyword_block = false elsif match = line.match(/^x-([^:\s]*)[:\s]*(.*)/i) keyword = match[1].strip.downcase arguments = match[2].to_s.strip.downcase.split(/[,; ]{1,}/) keywords << [keyword, arguments] in_keyword_block = true # Set this line to nil to have it stripped from the message. content_lines[i] = nil elsif in_keyword_block == true # Interpret line as arguments to the previous keyword. keywords[-1][-1] += line.downcase.strip.split(/[,; ]{1,}/) content_lines[i] = nil else # Any other line stops the keyword parsing. break end end [keywords, content_lines.compact] end # mail.signed? throws an error if it finds # pgp boundaries, so we must use the Mail::Gpg # methods. def encapsulated_signed?(mail) (mail.verify_result.nil? || mail.verify_result.signatures.empty?) && \ (Mail::Gpg.signed_mime?(mail) || Mail::Gpg.signed_inline?(mail)) end def add_footer!(footer_attribute) if self.list.blank? || self.list.send(footer_attribute).to_s.empty? return end footer_part = Mail::Part.new footer_part.body = self.list.send(footer_attribute).to_s if wrapped_single_text_part? self.parts.first.add_part footer_part else self.add_part footer_part end end def wrapped_single_text_part? parts.size == 1 && parts.first.mime_type == 'multipart/mixed' && parts.first.parts.size == 1 && parts.first.parts.first.mime_type == 'text/plain' end def _add_subject_prefix(suffix) attrib = 'subject_prefix' if suffix attrib << "_#{suffix}" end if ! self.list.respond_to?(attrib) return false end string = self.list.send(attrib).to_s.strip if ! string.empty? prefix = "#{string} " # Only insert prefix if it's not present already. if self.subject.nil? self.subject = string elsif ! self.subject.include?(prefix) self.subject = "#{prefix}#{self.subject}" end end end # Looking for signatures in each part. They are not aggregated into the main part. # We only return the signature if all parts are validly signed by the same key. def signature_multipart_inline fingerprints = parts.map do |part| if part.signature_valid? part.signature.fpr else nil end end if fingerprints.uniq.size == 1 parts.first.signature else nil end end def clutch_anglebrackets(input) Array(input).map do |string| if string.first == '<' string else "<#{string}>" end end.join(' ') end def detect_error_code # Detects the error code of an email with different heuristics # from: https://github.com/mailtop/bounce_email # Custom status codes unicode_subject = self.subject.to_s unicode_subject = unicode_subject.encode('utf-8') if unicode_subject.respond_to?(:encode) return '97' if unicode_subject.match(/delayed/i) return '98' if unicode_subject.match(/(unzulässiger|unerlaubter) anhang/i) return '99' if unicode_subject.match(/auto.*reply|férias|ferias|Estarei ausente|estou ausente|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i) # Feedback-Type: abuse return '96' if self.to_s.match(/Feedback-Type: abuse/i) if self.parts[1] match_parts = self.parts[1].body.match(/(Status:.|550 |#)([245]\.[0-9]{1,3}\.[0-9]{1,3})/) code = match_parts[2] if match_parts return code if code end # Now try getting it from correct part of tmail code = detect_bounce_status_code_from_text(self.body) return code if code # OK getting desperate so try getting code from entire email code = detect_bounce_status_code_from_text(self.to_s) code || 'unknown' end def bounce_detected? # Detects bounces from different parts of the email without error status codes # from: https://github.com/mailtop/bounce_email return true if self.subject.to_s.match(/(returned|undelivered) mail|mail delivery( failed)?|(delivery )(status notification|failure)|failure notice|undeliver(able|ed)( mail)?|return(ing message|ed) to sender/i) return true if self.subject.to_s.match(/auto.*reply|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i) return true if self['precedence'].to_s.match(/auto.*(reply|responder|antwort)/i) return true if self.from.to_s.match(/^(MAILER-DAEMON|POSTMASTER)@/i) false end def detect_bounce_status_code_from_text(text) # Parses a text and uses pattern matching to determines its error status (RFC 3463) # from: https://github.com/mailtop/bounce_email return '5.0.0' if text.match(/Status: 5\.0\.0/i) return '5.1.1' if text.match(/no such (address|user)|Recipient address rejected|User unknown|does not like recipient|The recipient was unavailable to take delivery of the message|Sorry, no mailbox here by that name|invalid address|unknown user|unknown local part|user not found|invalid recipient|failed after I sent the message|did not reach the following recipient|nicht zugestellt werden|o pode ser entregue para um ou mais/i) return '5.1.2' if text.match(/unrouteable mail domain|Esta casilla ha expirado por falta de uso|I couldn't find any host named/i) if text.match(/mailbox is full|Mailbox quota (usage|disk) exceeded|quota exceeded|Over quota|User mailbox exceeds allowed size|Message rejected\. Not enough storage space|user has exhausted allowed storage space|too many messages on the server|mailbox is over quota|mailbox exceeds allowed size|excedeu a quota/i) return '5.2.2' if text.match(/This is a permanent error||(Status: |)5\.2\.2/i) return '4.2.2' end return '5.1.0' if text.match(/Address rejected/) return '4.1.2' if text.match(/I couldn't find any host by that name/) return '4.2.0' if text.match(/not yet been delivered/i) return '5.1.1' if text.match(/mailbox unavailable|No such mailbox|RecipientNotFound|not found by SMTP address lookup|Status: 5\.1\.1/i) return '5.2.3' if text.match(/Status: 5\.2\.3/i) # Too messages in folder return '5.4.0' if text.match(/Status: 5\.4\.0/i) # too many hops return '5.4.4' if text.match(/Unrouteable address/i) return '4.4.7' if text.match(/retry timeout exceeded/i) return '5.2.0' if text.match(/The account or domain may not exist, they may be blacklisted, or missing the proper dns entries./i) return '5.5.4' if text.match(/554 TRANSACTION FAILED/i) return '4.4.1' if text.match(/Status: 4.4.1|delivery temporarily suspended|wasn't able to establish an SMTP connection/i) return '5.5.0' if text.match(/550 OU-002|Mail rejected by Windows Live Hotmail for policy reasons/i) return '5.1.2' if text.match(/PERM_FAILURE: DNS Error: Domain name not found/i) return '4.2.0' if text.match(/Delivery attempts will continue to be made for/i) return '5.5.4' if text.match(/554 delivery error:/i) return '5.1.1' if text.match(/550-5.1.1|This Gmail user does not exist/i) return '5.7.1' if text.match(/5.7.1 Your message.*?was blocked by ROTA DNSBL/i) # AA added return '5.7.2' if text.match(/not have permission to post messages to the group/i) return '5.3.2' if text.match(/Technical details of permanent failure|Too many bad recipients/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added return '4.3.2' if text.match(/Technical details of temporary failure/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added return '5.0.0' if text.match(/Delivery to the following recipient failed permanently/i) # AA added return '5.2.3' if text.match(/account closed|account has been disabled or discontinued|mailbox not found|prohibited by administrator|access denied|account does not exist/i) end end end schleuder-5.0.1/lib/schleuder/mail/parts_list.rb000066400000000000000000000004211502127241700216500ustar00rootroot00000000000000module Mail class PartsList # Disable sorting of mime-parts completely. # Can't be done during runtime on the body-instance (`body#set_sort_order`) # because MailGpg exchanges the body-instances when encrypting/signing. def sort!(*args) end end end schleuder-5.0.1/lib/schleuder/runner.rb000066400000000000000000000140601502127241700200570ustar00rootroot00000000000000module Schleuder class Runner def run(msg, recipient) error = setup_list(recipient) return error if error logger.info 'Parsing incoming email.' # is it valid utf-8? msg_scrubbed = false unless msg.valid_encoding? logger.warn 'Converting message due to invalid characters' detection = CharlockHolmes::EncodingDetector.detect(msg) begin msg = CharlockHolmes::Converter.convert(msg, detection[:encoding], 'UTF-8') rescue ArgumentError # it looks like even icu wasn't able to convert # so we scrub the invalid characters to be able to # at least parse the message somehow. Though this might # result in data loss. logger.warn 'Scrubbing message due to invalid characters' msg = msg.scrub msg_scrubbed = true end end @mail = Mail.create_message_to_list(msg, recipient, list) if msg_scrubbed @mail.add_pseudoheader(:note, I18n.t('pseudoheaders.scrubbed_message')) end error = run_filters('pre') if error if bounce?(error, @mail) return error else return nil end end begin # This decrypts, verifies, etc. @mail = @mail.setup rescue GPGME::Error::BadPassphrase, GPGME::Error::DecryptFailed, GPGME::Error::NoData, GPGME::Error::NoSecretKey, GPGME::Error::Canceled logger.warn 'Decryption of incoming message failed.' return Errors::DecryptionFailed.new(list) end error = run_filters('post') if error if bounce?(error, @mail) return error else return nil end end if ! @mail.was_validly_signed? logger.debug 'Message was not validly signed, adding subject_prefix_in' @mail.add_subject_prefix_in! end if ! @mail.was_encrypted? logger.debug 'Message was not encrypted, skipping keyword-handlers' elsif @mail.was_validly_signed? # run KeywordHandlers logger.debug 'Message was encrypted and validly signed' KeywordHandlersRunner.run(type: :list, list: list, mail: @mail).compact end # Don't send empty messages over the list. if @mail.empty? logger.info 'Message found empty, not sending it to list.' return Errors::MessageEmpty.new(@list) end logger.debug 'Adding subject_prefix' @mail.add_subject_prefix! # Subscriptions logger.debug 'Creating clean copy of message' copy = @mail.clean_copy(list.headers_to_meta.any?) list.send_to_subscriptions(copy, @mail) nil end private def list @list end def notify_admins(reason, original_message) if list.bounces_notify_admins list.logger.notify_admin reason, original_message, I18n.t('notice') end end def bounce?(response, mail) if list.bounces_drop_all list.logger.debug 'Dropping bounce as configurated' notify_admins(I18n.t('.bounces_drop_all'), mail.original_message) return false end list.bounces_drop_on_headers.each do |key, value| if mail[key].to_s.match(/#{value}/i) list.logger.debug "Incoming message header key '#{key}' matches value '#{value}': dropping the bounce." notify_admins(I18n.t('.bounces_drop_on_headers', key: key, value: value), mail.original_message) return false end end list.logger.debug 'Bouncing message' notify_admins("#{I18n.t('.bounces_notify_admins')}\n\n#{response}", mail.original_message) true end def run_filters(filter_type) filters_runner(filter_type).run(@mail) end def filters_runner(filter_type) if filter_type == 'pre' filters_runner_pre_decryption else filters_runner_post_decryption end end def filters_runner_pre_decryption @filters_runner_pre_decryption ||= Filters::Runner.new(list, 'pre') end def filters_runner_post_decryption @filters_runner_post_decryption ||= Filters::Runner.new(list, 'post') end def logger list.present? && list.logger || Schleuder.logger end def log_and_return(error, reveal_error=false) Schleuder.logger.error(error) if reveal_error error else # Return an unrevealing error, the sender and all bystanders don't need to know these details. Errors::FatalError.new end end def setup_list(recipient) return @list if @list logger.info "Loading list '#{recipient}'" if ! @list = List.by_recipient(recipient) return log_and_return(Errors::ListNotFound.new(recipient), true) end # Check necessary permissions of crucial files. if ! File.exist?(@list.listdir) return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_existing)) elsif ! File.directory?(@list.listdir) return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_a_directory)) elsif ! File.readable?(@list.listdir) return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_readable)) elsif ! File.writable?(@list.listdir) return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_writable)) else if File.exist?(@list.logfile) && ! File.writable?(@list.logfile) return log_and_return(Errors::ListdirProblem.new(@list.logfile, :not_writable)) end end # Check basic sanity of list. %w[fingerprint key secret_key admins].each do |attrib| if @list.send(attrib).blank? return log_and_return(Errors::ListPropertyMissing.new(@list.listdir, attrib)) end end # Set locale if I18n.available_locales.include?(@list.language.to_sym) I18n.locale = @list.language.to_sym end # This cannot be put in List, as Mail wouldn't know it then. logger.debug "Setting GNUPGHOME to #{@list.listdir}" ENV['GNUPGHOME'] = @list.listdir nil end end end schleuder-5.0.1/lib/schleuder/sks_client.rb000066400000000000000000000005571502127241700207120ustar00rootroot00000000000000module Schleuder class SksClient < Http SKS_PATH = '/pks/lookup?&exact=on&op=get&options=mr&search=SEARCH_ARG' class << self def get(input) super(url(input)) end private def url(input) arg = CGI.escape(input.gsub(/\s/, '')) Conf.sks_keyserver + SKS_PATH.gsub('SEARCH_ARG', arg) end end end end schleuder-5.0.1/lib/schleuder/subscription.rb000066400000000000000000000077521502127241700213040ustar00rootroot00000000000000module Schleuder class Subscription < ActiveRecord::Base belongs_to :list validates :list_id, inclusion: { in: -> (id) { List.pluck(:id) }, message: 'must refer to an existing list' } validates :email, presence: true, email: true validates :email, uniqueness: { scope: :list_id, case_sensitive: true } validates :fingerprint, allow_blank: true, fingerprint: true validates :delivery_enabled, :admin, boolean: true before_validation { self.email = Mail::Address.new(self.email).address self.email.downcase! if self.email.present? } default_scope { order(:email) } def to_s email end def self.configurable_attributes ['fingerprint', 'admin', 'delivery_enabled'] end def fingerprint=(arg) # Always assign the given value, because it must be possible to overwrite # the previous fingerprint with an empty value. That value should better # be nil instead of a blank string, but currently schleuder-cli (v0.1.0) expects # only strings. write_attribute(:fingerprint, arg.to_s.gsub(/\s*/, '').gsub(/^0x/, '').chomp.upcase) end def key # TODO: make key-related methods a concern, so we don't have to go # through the list and neither re-implement the methods here. # Prefix '0x' to force GnuPG to match only hex-values, not UIDs. @key ||= list.keys("0x#{self.fingerprint}").first end def send_mail(mail, incoming_mail=nil) list.logger.debug "Preparing sending to #{self.inspect}" mail = ensure_headers(mail, incoming_mail) gpg_opts = self.list.gpg_sign_options if self.key.blank? if self.list.send_encrypted_only? notify_of_missed_message(:absent) return false else list.logger.warn 'Sending plaintext because no key is present!' end elsif ! self.key.usable? if self.list.send_encrypted_only? notify_of_missed_message(key.usability_issue) return false else list.logger.warn "Sending plaintext because assigned key is #{key.usability_issue}!" end else gpg_opts.merge!(encrypt: true, keys: {self.email => "0x#{self.fingerprint}"}) end list.logger.info "Sending message to #{self.email}" mail.gpg gpg_opts mail.deliver end def ensure_headers(mail, incoming_mail=nil) mail.to = self.email if self.list.set_reply_to_to_sender? && ! incoming_mail.nil? # If the option "set_reply_to_to_sender" is set to true, we will set the reply-to header # to the reply-to header given by the original email. If no reply-to header exists in the original email, # the original senders email will be used as reply-to. if ! incoming_mail.reply_to.nil? mail.reply_to = incoming_mail.reply_to else mail.reply_to = incoming_mail.from end end if self.list.munge_from? && ! incoming_mail.nil? # If the option "munge_from" is set to true, we will add the original senders' from-header to ours. # We munge the from-header to avoid issues with DMARC. mail.from = I18n.t('header_munging', from: incoming_mail.from.first, list: self.list.email, list_address: self.list.email) else mail.from = self.list.email end mail.sender = self.list.bounce_address mail end def notify_of_missed_message(reason) self.list.logger.warn "Not sending to #{self.email}: key is unusable because it is #{reason} and sending plain text not allowed" mail = ensure_headers(Mail.new) mail.subject = I18n.t('notice') mail.body = I18n.t('missed_message_due_to_unusable_key', list_email: self.list.email) + I18n.t('errors.signoff') mail.gpg self.list.gpg_sign_options mail.deliver end def admin? self.admin == true end def delete_key list.delete_key(self.fingerprint) end end end schleuder-5.0.1/lib/schleuder/validators/000077500000000000000000000000001502127241700203705ustar00rootroot00000000000000schleuder-5.0.1/lib/schleuder/validators/boolean_validator.rb000066400000000000000000000003401502127241700243760ustar00rootroot00000000000000class BooleanValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if ! [true, false].include?(value) record.errors.add(attribute, I18n.t('errors.must_be_boolean')) end end end schleuder-5.0.1/lib/schleuder/validators/email_validator.rb000066400000000000000000000003641502127241700240540ustar00rootroot00000000000000class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ Conf::EMAIL_REGEXP record.errors.add(attribute, (options[:message] || I18n.t('errors.invalid_email'))) end end end schleuder-5.0.1/lib/schleuder/validators/fingerprint_validator.rb000066400000000000000000000004111502127241700253050ustar00rootroot00000000000000class FingerprintValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless GPGME::Key.valid_fingerprint?(value) record.errors.add(attribute, (options[:message] || I18n.t('errors.invalid_fingerprint'))) end end end schleuder-5.0.1/lib/schleuder/validators/greater_than_zero_validator.rb000066400000000000000000000003431502127241700264640ustar00rootroot00000000000000class GreaterThanZeroValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.to_i == 0 record.errors.add(attribute, I18n.t('errors.must_be_greater_than_zero')) end end end schleuder-5.0.1/lib/schleuder/validators/no_line_breaks_validator.rb000066400000000000000000000003361502127241700257360ustar00rootroot00000000000000class NoLineBreaksValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) if value.to_s.include?("\n") record.errors.add(attribute, I18n.t('errors.no_linebreaks') ) end end end schleuder-5.0.1/lib/schleuder/version.rb000066400000000000000000000000511502127241700202260ustar00rootroot00000000000000module Schleuder VERSION = '5.0.1' end schleuder-5.0.1/lib/schleuder/vks_client.rb000066400000000000000000000007651502127241700207160ustar00rootroot00000000000000module Schleuder class VksClient < Http VKS_PATH = '/vks/v1' class << self def get(type, input) if type.to_s == 'fingerprint' input = normalize_fingerprint(input) end super(url(type, input)) end private def normalize_fingerprint(input) input.gsub(/^0x/, '').gsub(/\s/, '').upcase end def url(type, input) "#{Conf.vks_keyserver}/#{VKS_PATH}/by-#{type}/#{CGI.escape(input)}" end end end end schleuder-5.0.1/locales/000077500000000000000000000000001502127241700151165ustar00rootroot00000000000000schleuder-5.0.1/locales/de.yml000066400000000000000000000426301502127241700162360ustar00rootroot00000000000000de: errors: attributes: language: inclusion: "muss einem der folgenden Werte entsprechen: en, de" log_level: inclusion: "muss einem der folgenden Werte entsprechen: debug, info, warn, error" openpgp_header_preference: inclusion: "muss einem der folgenden Werte entsprechen: sign, encrypt, signencrypt, unprotected, none" internal_footer: invalid: "enthält nicht druckbare Zeichen" public_footer: invalid: "enthält nicht druckbare Zeichen" invalid_email: "ist keine gültige E-Mail-Adresse" invalid_fingerprint: "ist kein gültiger OpenPGP-Fingerabdruck" list_fingerprint_missing: "Fingerabdruck der Liste ist nicht gesetzt, kann nicht verarbeiten! (In `%{listdir}`.)" list_key_missing: "Schlüssel der Liste nicht im Schlüsselring gefunden, kann nicht verarbeiten! (In `%{listdir}`.)" list_secret_key_missing: "Geheimer Schlüssel der Liste nicht im Schlüsselring gefunden, kann nicht verarbeiten! (In `%{listdir}`.)" list_admins_missing: "List hat keine Admins konfiguriert, kann nicht verarbeiten! (In `%{listdir}`.)" fatalerror: | Es ist ein schwerwiegender Fehler aufgetreten. Administratoren wurden benachrichtigt. Bitte versuche es später noch ein Mal. signoff: | Freundliche Grüße, Dein Schleuder-System. (Wenn du Hilfe brauchst lies bitte die Dokumentation (auf englisch) oder frage deine Listen- oder System-Admins.) decryption_failed: | Deine Email konnnte nicht entschlüsselt werden. Emails an diese Adresse müssen mit diesem Schlüssel verschlüsselt werden: %{key} Um den Schlüssel zugesandt zu bekommen sende eine Email an <%{sendkey_email}>. message_unsigned: Emails an diese Adresse müssen mit einem OpenPGP-Schlüssel signiert sein. message_signature_unknown: | Emails an diese Adresse müssen mit dem OpenPGP-Schlüssel signiert sein, der deiner Mitgliedschaft zugewiesen ist. Wenn du nicht weisst, welcher Schlüssel das ist, frage die Administrator/innen. Diese erreichst du per Email an <%{owner_email}>. (Vorzugsweise verschlüssele die Email mit dem Schlüssel dieser Adresse: %{list_fingerprint}). message_unencrypted: Emails an diese Adresse müssen OpenPGP-konform verschlüsselt sein. message_unauthenticated: Emails an diese Adresse müssen verschlüsselt und mit einem OpenPGP-Schlüssel signiert sein, der für eine Mitgliedschaft eingetragen ist. message_sender_not_subscribed: Nur Absender, die als Mitglied eingetragen sind, dürfen Emails an diese Adresse schicken. message_not_from_admin: Nur Admins dürfen Emails an diese Adresse schicken. message_empty: | Deine Email enthielt keinen Text, daher wurde sie nicht über die Liste verteilt. Falls du ausschließlich Schlüsselwörter gesendet hast beachte, dass administrative Schlüsselwörter an die "request"-Adresse (<%{request_address}>) geschickt werden müssen um berücksichtigt zu werden. list_not_found: "Fehler: Keine Liste zu dieser Adresse gefunden: '%{email}'." no_linebreaks: "Darf keine Zeilenumbrüche enthalten" invalid_characters: "enthält ungültige Zeichen" listdir_problem: message: "Problem mit dem Listen-Verzeichnis: '%{dir}' %{problem}." not_existing: existiert nicht not_a_directory: ist kein Verzeichnis not_empty: ist nicht leer not_writable: ist nicht beschreibbar not_readable: ist nicht lesbar keyword_admin_only: Das Schlüsselwort '%{keyword}' darf nur von Listen-Admins verwendet werden. unknown_keyword: Das verwendete Schlüsselwort '%{input}' ist unbekannt. Bitte prüfe seine Schreibweise oder die Dokumentation. key_generation_failed: Das Erzeugen des OpenPGP-Schlüsselpaares für %{listname} ist aus unbekannten Gründen fehlgeschlagen. Bitte prüfe das Listen-Verzeichnis ('%{listdir}') und die Log-Dateien. key_adduid_failed: "Das Hinzufügen einer User-ID zum OpenPGP-Schlüssel ist mit folgender Meldung fehlgeschlagen:\n%{errmsg}" too_many_keys: "Fehler: In %{listdir} existieren mehrere OpenPGP-Schlüssel für %{listname}. Bitte lösche alle bis auf einen." loading_list_settings_failed: "%{config_file} konnte nicht eingelesen werden, bitte Formatierung auf gültiges YAML prüfen." message_too_big: "Deine Email war zu groß. Erlaubt sind für diese Liste %{allowed_size}KB." must_be_boolean: "muss true oder false sein" must_be_greater_than_zero: "muss größer als null sein" not_pgp_mime: "Deine Email war nicht im pgp/mime-Format verschlüsselt." delivery_error: "Beim Versenden einer Email an %{email} ist der folgende Fehler aufgetreten: %{error}" no_match_for: "Keine Treffer für %{input}" too_many_matching_keys: | Zu viele Schlüssel gefunden für '%{input}': %{key_strings} keyword_handlers: handler_failed: Das Schlüsselwort '%{keyword}' verursachte einen unbekannten Fehler. Die System-Administratoren wurden benachrichtigt. keyword_admin_notify: request: | %{sender} benutzte dieses Schlüsselwort: %{keyword}: %{arguments} ...und erhielt dies als Antwort: {response} list: | %{sender} schickte dieses Schlüsselwort an die Liste: %{keyword}: %{arguments} key_management: key_not_found: "Fehler: Keinen Schlüssel mit Fingerabdruck '%{fingerprint}' gefunden." deleted: "Gelöscht: %{key_string}" not_deletable: "Darf nicht gelöscht werden: %{key_string}" no_imports: In deiner Email konnten keine Schlüssel gefunden werden. :( key_import_status: imported: | Dieser Schlüssel wurde neu hinzugefügt: %{key_summary} updated: | Dieser Schlüssel wurde aktualisiert: %{key_summary} unchanged: | Dieser Schlüssel wurde nicht verändert: %{key_summary} error: | Der Schlüssel mit diesem Fingerabdruck konnte aus unbekanntem Grund nicht hinzugefügt werden: %{fingerprint} matching_keys_intro: Alle Schlüssel aus dem Schlüsselring der Liste, die '%{input}' enthalten, sind an diese Email angehängt. delete_key_requires_arguments: | Fehler: Du hast zu dem Schlüsselwort 'DELETE-KEY' keinen Wert angegeben. Ein Wert ist nötig, weitere sind optional. Bspw.: X-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Oder, um mehrere Schlüssel auf einmal zu löschen: X-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 a-subscription@hostname Die Schlüssel werden nur gelöscht, wenn sie der einzige Treffer für den jeweiligen Wert sind. fetch_key_requires_arguments: | Fehler: Du hast zu dem Schlüsselwort 'FETCH-KEY' keinen Wert angegeben. Ein Wert ist nötig, weitere sind optional. Jeder Wert kann eine URL sein, die via HTTP geladen wird, oder ein Text, mit dem auf den OpenPGP-Schlüsselserver gesucht wird. Bspw., um einen Schlüssel per fingerprint vom Schlüsselserver zu holen: X-FETCH-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Oder, um zwei Schlüssel per Email-Adresse vom Schlüsselserver zu holen: X-FETCH-KEY: a-subscription@hostname anotherone@example.org Oder, um einen Schlüssel per HTTP von einem Server zu laden: X-FETCH-KEY: https://example.org/keys/mykey.asc no_content_found: Deine Email enthielt keine Anhänge und keinen Text-Inhalt, daher konnte kein Schlüssel importiert werden. resend: not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar. Unverschlüsseltes Senden verboten). not_resent_encrypted_no_keys: Verschlüsseltes Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar). aborted: Resending an <%{email}> abgebrochen aufgrund anderer Probleme. encrypted_to: Verschlüsselt an unencrypted_to: Unverschlüsselt an invalid_recipient: "Ungültige Emailadresse für resend: %{address}" subscription_management: forbidden: "Fehler: Du bist nicht berechtigt, die Mitgliedschaft von %{email} zu beenden." is_not_subscribed: Keine Mitgliedschaft von %{email} gefunden. unsubscribed: Die Mitgliedschaft von %{email} wurde gelöscht. unsubscribing_failed: | Mitgliedschaft von %{email} nicht gelöscht: %{errors} cannot_unsubscribe_last_admin: | %{email} ist die einzige Admin-Mitgliedschaft für diese Liste, daher darf sie nicht gelöscht werden. subscribed: | Mitgliedschaft von %{email} mit diesen Werten eingetragen: Fingerabdruck: %{fingerprint} Admin? %{admin} Email-Zustellung aktiv? %{delivery_enabled} subscribing_failed: | Die Mitgliedschaft von %{email} konnte aufgrund eines Fehlers nicht eingetragen werden: %{errors}. list_of_subscriptions: "Mitgliedschaften:\n" set_fingerprint_only_self: Nur Admins dürfen den Fingerabdruck für andere Mitgliedschaften festlegen. fingerprint_set: Fingerabdruck für %{email} auf %{fingerprint} gesetzt. setting_fingerprint_failed: | Fingerabdruck für %{email} konnte nicht auf %{fingerprint} gesetzt werden: %{errors}. set_fingerprint_requires_valid_fingerprint: | Du hast zu dem Schlüsselwort 'SET-FINGERPRINT' keinen gültigen Wert angegeben. Es wurde der folgende Wert erkannt: %{fingerprint} Benötigt werden ein oder zwei Werte, bspw.: X-SET-FINGERPRINT: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 oder (als Admin): X-SET-FINGERPRINT: subscription2@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Wobei der Fingerprint in der gesamten Länge (40 Zeichen) angegeben werden muss. Optional mit 0x als Präfix. Um einen Fingerprint zu entfernen kannst du das Schlüsselwort 'UNSET-FINGERPRINT' verwenden. set_fingerprint_requires_arguments: | Du hast zu dem Schlüsselwort 'SET-FINGERPRINT' keinen Wert angegeben. Benötigt werden ein oder zwei Werte, bspw.: X-SET-FINGERPRINT: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 oder (als Admin): X-SET-FINGERPRINT: subscription2@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Um einen Fingerprint zu entfernen kannst du das Schlüsselwort 'UNSET-FINGERPRINT' verwenden. unset_fingerprint_only_self: Nur Admins dürfen den Fingerabdruck für andere Mitgliedschaften löschen. fingerprint_unset: Fingerabdruck für %{email} wurde entfernt. unsetting_fingerprint_failed: | Fingerabdruck für %{email} konnte nicht entfernt werden: %{errors}. unset_fingerprint_requires_arguments: | Du hast zu dem Schlüsselwort 'UNSET-FINGERPRINT' keinen Wert angegeben. Benötigt werden ein Wert, bspw.: X-UNSET-FINGERPRINT: subscription2@hostname Als Admin musst du um deinen eigenen Fingerabdruck zu entfernen, noch zusätzlich das Argument force mitgeben. bspw.: X-UNSET-FINGERPRINT: adminsubscription2@hostname force subscribe_requires_arguments: | Fehler: Du hast zu dem Schlüsselwort 'SUBSCRIBE' keine passenden Werte angegeben. Mindestens ein Wert ist nötig, drei weitere sind optional. Bspw.: X-SUBSCRIBE: new-subscription@hostname Oder, um den Schlüssel der neuen Mitgliedschaft zuzuweisen: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Oder, um den Schlüssel zuzuweisen, und die Mitgliedschaft als Admin einzutragen: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 true Oder, um den Schlüssel zuzuweisen, die Mitgliedschaft als Admin einzutragen, und die Zustellung von Listen-Emails für diese Mitgliedschaft abzuschalten: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 true false Wenn du die optionalen Werte weglässt hat die Mitgliedschaft keinen Schlüssel zugewiesen, ist nicht Admin, und hat die Zustellung von Listen-Email aktiviert. sign_this: signatures_attached: Die Signaturen hängen an. no_content_found: Deine Email enthielt keine Anhänge und keinen Text-Inhalt, daher konnte nichts signiert werden. list_management: no_logfile: Keine Log-Datei für %{listname} gefunden. (Das kann an einem hohem Log-Level und der Abwesenheit von Fehlern liegen.) logfile_attached: Die Log-Datei für %{listname} hängt an. list_public_key_subject: Schlüssel dieser Adresse list_public_key_attached: Der Schlüssel zu dieser Adresse hängt an. no_output_result: Deine Email ergab keinen Ausgabe-Text. owner_forward_prefix: Die folgende Email ging für die Listen-Besitzer/innen ein. no_keywords_error: Deine Email enthielt keine Schlüsselwörter, daher gab es nichts zu tun. missing_listname_keyword_error: Deine Email enthielt nicht das notwendige Schlüsselwort "X-LIST-NAME", daher wurde sie zurückgewiesen. wrong_listname_keyword_error: Deine Email enthielt ein falsches "X-LIST-NAME"-Schlüsselwort. Der Wert dieses Schlüsselworts muss der Emailadresse dieser Liste gleichen. bounces_drop_all: Die angehängte Email hätte zurückgewiesen (bounced) werden sollen, wurde aber stillschweigend fallen gelassen, weil die Konfiguration dieser Liste definiert, dass für diese Liste nie Email zurückgewiesen werden soll. bounces_drop_on_headers: "Die angehängte Email hätte zurückgewiesen (bounce) werden sollen, wurde aber stillschweigend fallen gelassen, weil diese Kopfzeile gefunden wurde: %{key}: %{value}" bounces_notify_admins: "Die angehängte Email wurde mit folgender Nachricht zurückgewiesen:" notice: Hinweis incoming_message: Eingehende Email forward_all_incoming_to_admins: Die angehängte Email ging ein. forward_automated_message_to_admins: Die angehängte Email wurde vermutlich von einer Maschine und nicht von einem Menschen versendet. Daher ist sie nicht über die Liste verteilt sondern an euch Admins weitergeleitet worden. automated_message_subject: Automatische Nachricht empfangen check_keys: Schlüsselprüfung check_keys_intro: "Bitte kümmere dich um die folgenden Schlüssel für Liste %{email}." key_expires: | Dieser Schlüssel läuft in %{days} Tagen ab: %{key_summary} key_unusable: | Dieser Schlüssel ist %{usability_issue}: %{key_summary} missed_message_due_to_unusable_key: | Du hast eine Email von %{list_email} verpasst weil deiner Mitgliedschaft kein (benutzbarer) OpenPGP-Schlüssel zugewiesen ist. Bitte kümmere dich darum. Das kannst du mit dem Schlüsselwort X-SET-FINGERPRINT tun. Siehe (englisch) für Details. Falls "schleuder-web" für dieses Schleuder-System eingerichtet wurde kannst du es auch über den Browser lösen (das wissen deine Listen- oder System-Admins). refresh_keys: Schlüsselaktualisierung refresh_keys_intro: "Die Aktualisierung aller Schlüssel des Schlüsselrings für Liste %{email} ergab dies:" key_updated: | Dieser Schlüssel wurde aktualisiert (%{states}): %{key_summary} key_fetched: | Dieser Schlüssel wurde geholt (%{states}): %{key_summary} import_states: unchanged: unverändert new_key: neuer Schlüssel new_uids: neue User-IDs new_subkeys: neue Unterschlüssel new_signatures: neue Signaturen key_fetcher: invalid_input: "Ungültige Angabe. Gültig sind: URLs, OpenPGP-Fingerabdrücke, oder Emailadressen." not_found: "Fehler: Keinen Schlüssel für '%{input}' gefunden." url_not_found: "Fehler: Unter <%{url}> wurde nichts gefunden (404 Not Found)." general_error: "Fehler beim Holen der Daten aus dem Netz: %{error}" import_error: "Fehler beim Importieren der geholten Daten: %{error}" email_key_importer: key_already_present: "Für die Absenderadresse dieser Email hat diese Liste schon einen anderen Schlüssel, daher wurde der Schlüssel aus dieser Email nicht hinzugefügt." import_error: "Fehler beim Import von Schlüssel %{fingerprint} aus dieser Email: %{import_states}." technical_error: "Beim Schlüsselimport aus dieser Email ist ein unerwarteter Fehler aufgetreten." key_added: "Dieser Schlüssel wurde aus dieser Email neu hinzugefügt: %{key_summary}" key_unchanged: "Dieser Schlüssel wurde aus dieser Email nicht verändert: %{key_summary}." key_updated: "Dieser Schlüssel wurde aus dieser Email aktualisiert: %{key_summary}." pseudoheaders: scrubbed_message: Diese Email enthielt ungültige Zeichen, die aus Verarbeitungsgründen möglicherweise entfernt wurden. stripped_html_from_multialt: Diese Email enthielt einen alternativen HTML-Teil, der PGP-Daten beinhaltete. Der HTML-Teil wurde entfernt, um die Email sauberer analysieren zu können. stripped_html_from_multialt_with_keywords: Diese Email enthielt Schlüsselwörter und einen alternativen HTML-Teil. Der HTML-Teil wurde entfernt, um zu verhindern dass diese Schlüsselwörter Aussenstehenden bekannt werden. signature_states: unknown: "Unbekannte Signatur von unbekanntem Schlüssel 0x%{fingerprint}" unsigned: "Unsigniert" encryption_states: encrypted: "Verschlüsselt" unencrypted: "Unverschlüsselt" header_munging: "%{from} über %{list} <%{list_address}>" activerecord: errors: models: schleuder/subscription: attributes: email: taken: 'ist schon eingetragen' schleuder-5.0.1/locales/en.yml000066400000000000000000000366251502127241700162570ustar00rootroot00000000000000en: errors: attributes: language: inclusion: "must be one of: en, de" log_level: inclusion: "must be one of: debug, info, warn, error" openpgp_header_preference: inclusion: "must be one of: sign, encrypt, signencrypt, unprotected, none" internal_footer: invalid: "includes non-printable characters" public_footer: invalid: "includes non-printable characters" invalid_email: "is not a valid email address" invalid_fingerprint: "is not a valid OpenPGP-fingerprint" list_fingerprint_missing: "List has no fingerprint configured, cannot run! (In `%{listdir}`.)" list_key_missing: "List-key is missing in keyring, cannot run! (In `%{listdir}`.)" list_secret_key_missing: "Secret key of list is missing in keyring, cannot run! (In `%{listdir}`.)" list_admins_missing: "List has no admins configured, cannot run! (In `%{listdir}`.)" fatalerror: | A fatal error happened. Administrators have been notified. Please try again later. signoff: | Kind regards, Your Schleuder system. (If you need help please read the documentation or ask your list- or server-admins.) decryption_failed: | Decrypting your message failed. Messages to this address must be encrypted with the following key: %{key} To receive it send an email to <%{email}>. message_unsigned: Messages to this address must be OpenPGP-signed. message_signature_unknown: | Messages to this address must be OpenPGP-signed by the key that is configured for your subscription. If you don't know which one that is, ask an administrator of this list. You can contact the administrators by sending a message to <%{owner_email}> (preferably encrypt it with this addresses' public key: %{list_fingerprint}). message_unencrypted: Messages to this address must be encrypted conforming to OpenPGP. message_unauthenticated: Messages to this address must be encrypted and signed by the key associated with a subscribed address. message_sender_not_subscribed: Only subscribed addresses may send messages to this address. message_not_from_admin: Only admins may send messages to this address. message_empty: | Your message was found empty and wasn't passed on to the list. In case you only sent keywords please note that administrative keywords must be sent to the "request"-address (<%{request_address}>) in order to be respected. no_linebreaks: "must not include line-breaks" list_not_found: "Error: No list found with this address: '%{email}'." invalid_characters: "contains invalid characters" listdir_problem: message: "There's a problem with the list-directory: '%{dir}' %{problem}." not_existing: does not exist not_a_directory: is not a directory not_empty: is not empty not_writable: is not writable not_readable: is not readable keyword_admin_only: The keyword '%{keyword}' may only be used by list-admin. unknown_keyword: The given keyword '%{keyword}' is unknown. Please check its spelling or the documentation. key_generation_failed: Generating the OpenPGP key pair for %{listname} failed for unknown reasons. Please check the list-directory ('%{listdir}') and the log-files. key_adduid_failed: "Adding a user-ID to the OpenPGP key failed with this message:\n%{errmsg}" too_many_keys: "Error: In %{listdir} there's more than one matching OpenPGP-key for %{listname}. Please delete all but one." loading_list_settings_failed: "%{config_file} could not be parsed, please check its formatting to be valid YAML." message_too_big: "Your message was too big. Allowed are up to %{allowed_size}KB." must_be_boolean: "must be true or false" must_be_greater_than_zero: "must be a number greater than zero" not_pgp_mime: "Message was not encrypted in the pgp/mime-format." delivery_error: "The following error occurred while sending a message to %{email}: %{error}" no_match_for: "No match found for %{input}" too_many_matching_keys: | Too many matching keys for '%{input}': %{key_strings} keyword_handlers: handler_failed: Running keyword '%{keyword}' caused an unknown error. System-admins have been notified. keyword_admin_notify: request: | %{sender} sent this keyword: %{keyword}: %{arguments} ...and received this response: %{response} list: | %{sender} sent this keyword to the list: %{keyword}: %{arguments} key_management: key_not_found: "Error: No key found with this fingerprint: '%{fingerprint}'." deleted: | This key was deleted: %{key_string} not_deletable: | This key may not be deleted: %{key_string} no_imports: In the message you sent us, no keys could be found. :( key_import_status: imported: | This key was newly added: %{key_summary} updated: | This key was updated: %{key_summary} unchanged: | This key was not changed: %{key_summary} error: | The key with this fingerprint could not be added due to an unknown error: %{fingerprint} matching_keys_intro: All keys from the list's keyring matching '%{input}' are attached to this message. delete_key_requires_arguments: | Error: You did not send any arguments for the keyword 'DELETE-KEY'. One is required, more are optional, e.g.: X-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Or, to delete multiple keys at once: X-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 a-subscription@hostname The matching keys will be deleted only if the argument matches them distinctly. fetch_key_requires_arguments: | Error: You did not send any arguments for the keyword 'FETCH-KEY'. One is required, more are optional. Each argument can be an URL to fetch via HTTP, or a string to look up at the OpenPGP-keyservers. E.g., to fetch a key by fingerprint from the keyserver: X-FETCH-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Or, to fetch two keys by email-address from the keyserver: X-FETCH-KEY: a-subscription@hostname anotherone@example.org Or, to fetch a key keys by URL: X-FETCH-KEY: https://example.org/keys/mykey.asc no_content_found: Your message did not contain any attachments nor text content. Therefore no key could be imported. resend: not_resent_no_keys: Resending to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used. Unencrypted sending not allowed). not_resent_encrypted_no_keys: Resending as encrypted email to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used). aborted: Resending to <%{email}> aborted due to other errors. encrypted_to: Encrypted to unencrypted_to: Unencrypted to invalid_recipient: "Invalid email-address for resending: %{address}" subscription_management: forbidden: "Error: You're not allowed to unsubscribe %{email}." is_not_subscribed: "%{email} is not subscribed." unsubscribed: "%{email} has been unsubscribed." unsubscribing_failed: | Unsubscribing %{email} failed: %{errors} cannot_unsubscribe_last_admin: | %{email} is the only admin for this list, thus it can not be unsubscribed. subscribed: | %{email} has been subscribed with these attributes: Fingerprint: %{fingerprint} Admin? %{admin} Email-delivery enabled? %{delivery_enabled} subscribing_failed: | Subscribing %{email} failed: %{errors}. list_of_subscriptions: "Subscriptions:\n" set_fingerprint_only_self: Only admins may set fingerprints of subscriptions other than their own. fingerprint_set: Fingerprint for %{email} set to %{fingerprint}. setting_fingerprint_failed: | Setting fingerprint for %{email} to %{fingerprint} failed: %{errors}. set_fingerprint_requires_valid_fingerprint: | You did not send a valid fingerprint for the keyword 'SET-FINGERPRINT' The following value was detected: %{fingerprint} One or two are required, e.g.: X-SET-FINGERPRINT: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 or (as an admin): X-SET-FINGERPRINT: subscription2@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 While the fingerprint must be passed in the full length (40 characters). Optionally prefixed with 0x. To remove a fingerprint you can use the keyword 'UNSET-FINGERPRINT' set_fingerprint_requires_arguments: | Error: You did not send any arguments for the keyword 'SET-FINGERPRINT'. One or two are required, e.g.: X-SET-FINGERPRINT: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 or (as an admin): X-SET-FINGERPRINT: subscription2@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 To remove a fingerprint you can use the keyword 'UNSET-FINGERPRINT' unset_fingerprint_only_self: Only admins may remove fingerprints of subscriptions other than their own. fingerprint_unset: Fingerprint for %{email} removed. unsetting_fingerprint_failed: | Removing fingerprint for %{email} failed: %{errors}. unset_fingerprint_requires_arguments: | Error: You did not send any arguments for the keyword 'UNSET-FINGERPRINT' One value is required, e.g.: X-UNSET-FINGERPRINT: subscription2@hostname As an admin to unset your own fingerprint you must additionally pass the argument force. E.g.: X-UNSET-FINGERPRINT: adminsubscription2@hostname force subscribe_requires_arguments: | Error: You did not send proper arguments for the keyword 'SUBSCRIBE'. At least one argument is required, three more are optional. E.g.: X-SUBSCRIBE: new-subscription@hostname Or, defining the key to use for the new subscription: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 Or, defining the key to use, and setting this subscription to be an admin: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 true Or, defining the key to use, setting this subscription as admin, and disabling the delivery of list-emails to this subscription: X-SUBSCRIBE: new-subscription@hostname 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 true false If you omit the optional arguments, the subscription has no key assigned, is not an admin, and has the delivery of list-emails enabled. sign_this: signatures_attached: Find the signatures attached. no_content_found: Your message did not contain any attachments nor text content. Therefore nothing could be signed. list_management: no_logfile: No logfile could be found for %{listname}. (This might be caused by a high log-level and the absence of errors.) logfile_attached: The logfile for %{listname} is attached. list_public_key_subject: Key for this address list_public_key_attached: Find the key for this address attached. no_output_result: Your message resulted in no output. owner_forward_prefix: The following message was received for the list-owners. no_keywords_error: Your message didn't contain any keywords, thus there was nothing to do. missing_listname_keyword_error: Your message did not contain the required "X-LIST-NAME" keyword and was rejected. wrong_listname_keyword_error: Your message contained an incorrect "X-LIST-NAME" keyword. The keyword argument must match the email address of this list. bounces_drop_all: The attached message should have been bounced but was dropped without further notice because the list's configuration defines that no message should ever be bounced. bounces_drop_on_headers: "The attached message should have been bounced but was dropped without further notice because it matched this header-line: %{key}: %{value}" bounces_notify_admins: "The attached message was bounced with the following notice:" notice: Notice incoming_message: Incoming message forward_all_incoming_to_admins: The attached message was received. forward_automated_message_to_admins: Attached is a message that probably was sent by a machine, not a human. Therefore it has not been passed on to the list, but only to you, the admins. automated_message_subject: Automated message received check_keys: Keys check check_keys_intro: "Please take care of these keys for list %{email}." key_expires: | This key expires in %{days} days: %{key_summary} key_unusable: | This key is %{usability_issue}: %{key_summary} missed_message_due_to_unusable_key: | You missed an email from %{list_email} because your subscription isn't associated with a (usable) OpenPGP key. Please fix this. To do that you can use the keyword X-SET-FINGERPRINT, see for further explanation. If "schleuder-web" has been setup for this host you might also use your browser (your list- or system-admins can tell you that). refresh_keys: Keys update refresh_keys_intro: "Refreshing all keys from the keyring of list %{email} resulted in this:" key_updated: | This key was updated (%{states}): %{key_summary} key_fetched: | This key was fetched (%{states}): %{key_summary} import_states: unchanged: unchanged new_key: new key new_uids: new user-IDs new_subkeys: new subkeys new_signatures: new signatures key_fetcher: invalid_input: "Invalid input. Allowed are: URLs, OpenPGP-fingerprints, or email-addresses." not_found: "Error: No key could be found for '%{input}'." url_not_found: "Error: There's nothing at <%{url}> (404 Not Found)." general_error: "Error while fetching data from the internet: %{error}" import_error: "Error while importing the fetched data: %{error}" email_key_importer: key_already_present: "The list already knows a different key for this email's sender address, therefore the key from this email was not added." import_error: "Error while importing key %{fingerprint} from this email: %{import_states}." technical_error: "Unexpected technical error while importing key from this email." key_added: "This key was newly added from this email: %{key_summary}." key_unchanged: "This key was unchanged from this email: %{key_summary}." key_updated: "This key was updated from this email: %{key_summary}." pseudoheaders: scrubbed_message: This message included invalid characters, which might have been removed to be able to process the message properly. stripped_html_from_multialt: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly. stripped_html_from_multialt_with_keywords: This message included keywords and an alternating HTML-part. The HTML-part was removed to prevent the disclosure of these keywords to third parties. signature_states: unknown: "Unknown signature by unknown key 0x%{fingerprint}" unsigned: "Unsigned" encryption_states: encrypted: "Encrypted" unencrypted: "Unencrypted" header_munging: "%{from} via %{list} <%{list_address}>" activerecord: errors: models: schleuder/subscription: attributes: email: taken: 'is already subscribed' schleuder-5.0.1/man/000077500000000000000000000000001502127241700142475ustar00rootroot00000000000000schleuder-5.0.1/man/schleuder-api-daemon.8000066400000000000000000000033451502127241700203330ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SCHLEUDER\-API\-DAEMON" "8" "January 2017" "" "" . .SH "NAME" \fBschleuder\-api\-daemon\fR \- provides the API of schleuder(8) . .SH "SYNOPSIS" \fBschleuder\-api\-daemon\fR . .SH "DESCRIPTION" schleuder\-api\-daemon provides the HTTP\-API of \fBschleuder(8)\fR to clients\. . .SH "ENVIRONMENT" . .SS "Configuration" \fBschleuder\-api\-daemon\fR reads configuration out of the \fBapi\fR section of \fBschleuder\fR\'s configuration\. Please see \fBschleuder\fR(8) for details about the configuration file\. . .P The available options are: . .TP \fBhost\fR The hostname/IP to listen at\. . .TP \fBport\fR The port to listen at\. Default: 4443\. . .TP \fBtls_cert_file\fR Path to the file that contains the TLS\-certificate to use\. You can generate a new one with \fBschleuder cert generate\fR\. . .TP \fBtls_key_file\fR Path to the file that contains the TLS\-key to use\. . .TP \fBvalid_api_keys\fR List of api_keys to allow access to the API\. . .SS "Clients" Available clients using the API are \fBschleuder\-cli\fR(8) and \fBschleuder\-web\fR\. URLs to their websites are listed below (\fISEE ALSO\fR)\. . .SH "BUGS" Known bugs are listed on the Schleuder bugtracker at \fIhttps://0xacab\.org/schleuder/schleuder\fR . .SH "SEE ALSO" \fBschleuder\fR(8), \fBschleuder\-cli\fR(8) . .TP Website of \fBschleuder\fR \fIhttps://schleuder\.org/\fR . .TP More extensive documentation for \fBschleuder\fR \fIhttps://schleuder\.org/docs/\fR . .TP \fBschleuder\-cli\fR, the command line interface for list\-management \fIhttps://0xacab\.org/schleuder/schleuder\-cli/\fR . .TP \fBschleuder\-web\fR, the web interface for list\-management \fIhttps://0xacab\.org/schleuder/schleuder\-web/\fR schleuder-5.0.1/man/schleuder-api-daemon.8.ron000066400000000000000000000027701502127241700211310ustar00rootroot00000000000000schleuder-api-daemon(8) -- provides the API of schleuder(8) =========================================================== ## SYNOPSIS `schleuder-api-daemon` ## DESCRIPTION schleuder-api-daemon provides the HTTP-API of `schleuder(8)` to clients. ## ENVIRONMENT ### Configuration `schleuder-api-daemon` reads configuration out of the `api` section of `schleuder`'s configuration. Please see `schleuder`(8) for details about the configuration file. The available options are: * `host`: The hostname/IP to listen at. * `port`: The port to listen at. Default: 4443. * `tls_cert_file`: Path to the file that contains the TLS-certificate to use. You can generate a new one with `schleuder cert generate`. * `tls_key_file`: Path to the file that contains the TLS-key to use. * `valid_api_keys`: List of api_keys to allow access to the API. ### Clients Available clients using the API are `schleuder-cli`(8) and `schleuder-web`. URLs to their websites are listed below ([SEE ALSO][]). ## BUGS Known bugs are listed on the Schleuder bugtracker at ## SEE ALSO `schleuder`(8), `schleuder-cli`(8) * Website of `schleuder`: * More extensive documentation for `schleuder`: * `schleuder-cli`, the command line interface for list-management: * `schleuder-web`, the web interface for list-management: schleuder-5.0.1/man/schleuder.8000066400000000000000000000143511502127241700163220ustar00rootroot00000000000000.\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . .TH "SCHLEUDER" "8" "January 2017" "" "" . .SH "NAME" \fBschleuder\fR \- an email hub for groups . .SH "SYNOPSIS" \fBschleuder\fR work \fIlistaddress\fR < \fIemail\fR . .P \fBschleuder\fR help . .P \fBschleuder\fR \fIother\-command\fR [<\.\.\.>] . .P For descriptions of the other commands see the output of the \fIhelp\fR\-command\. . .P This manual page is written for system administrators\. For other information please read the Schleuder documentation online (\fISEE ALSO\fR)\. . .SH "DESCRIPTION" Schleuder is an email hub for groups\. Subscribers can communicate encryptedly and pseudonymously among themselves, receive emails from non\-subscribers and send emails to non\-subscribers via the list\. . .P Schleuder takes care of all de\- and encryption, stripping of headers, formatting conversions, etc\. Further Schleuder can send out its own public key and receive administrative commands by email\. . .P Email cryptography is handled by using GnuPG\. . .SH "ENVIRONMENT" . .SS "Configuration" Schleuder reads its basic settings from a file that it by default expects at \. To make Schleuder read a different file set the environment variable \fISCHLEUDER_CONFIG\fR to the path to your file when running schleuder\. E\.g\.: . .IP "" 4 . .nf SCHLEUDER_CONFIG=/usr/local/etc/schleuder\.yml /path/to/bin/schleuder \.\.\. . .fi . .IP "" 0 . .P For explanations of the possible settings read the default config file\. . .P The default settings for new lists are read from another config file\. By default Schleuder looks at \. To make Schleuder read a different file set the environment variable \fISCHLEUDER_LIST_DEFAULTS\fR analogous to above\. The possible settings are explained in the default config file\. . .SS "Connect to MTA" Schleuder behaves like an email\-filter: it reads email from standard\-input, and reports errors to standard\-error\. If all goes well Schleuder closes the initial connection to the Mail Transport Agent (MTA) only after it sent out all outgoing emails\. . .P In case of an error the MTA is expected to include Schleuder\'s error message into a bounce\-email that is sent back to the sender (this is default behaviour of most MTAs)\. . .P To connect the MTA with Schleuder it must pipe the incoming message into Schleuder\'s STDIN, and give it two arguments: first one: "work", second one: the full recipients email address\. . .P For more information on how to integrate Schleuder with your existing mail setup, please read the Schleuder documentation online (\fISEE ALSO\fR)\. . .SS "Data storage" The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under \fIlists_dir\fR/\fIhostname\fR/\fIlistname\fR/ (\fIlists_dir\fR is read from schleuder\.yml, by default it is )\. They can be used manually using gpg2\. Please be careful to maintain proper file permissions if you touch the files\. . .P In the list\-directory there’s also a list specific log\-file (might be missing if the log\-level is high and no error occurred yet)\. . .P Other logging is sent to syslog\. Where that ends up depends on the operating system and the system administration\. . .P All other list\-related data is stored in the SQL\-database\. Most data is unserialized, only some values are JSON\-encoded\. . .SH "SPECIAL FEATURES" Schleuder features some special functionality\. For more detailed information read the Schleuder documentation online (\fISEE ALSO\fR)\. . .SS "Getting the public key of a list" Each Schleuder\-list replies with its public key to any email sent to \fIlistname\-sendkey@hostname\fR\. E\.g\. to receive the key for the contact address of the Schleuder project write an email to . .br \fIteam\-sendkey@schleuder\.org\fR\. . .SS "Email commands" Schleuder knows some special keywords that trigger different behaviour\. You can e\.g\. subscribe someone, or resend an email to a non\-subscriber using keywords\. . .P Keywords require that: . .IP "\(bu" 4 they start the line and begin with "x\-", . .IP "\(bu" 4 they are written into the beginning of the \fIfirst text\-part\fR of the email (usually that’s just the normal body of the email), . .IP "\(bu" 4 possible arguments must be written \fIon the same line\fR as the keyword (exceptions are mentioned in the descriptions below), . .IP "\(bu" 4 the email must be \fIencrypted and signed\fR by a list\-member’s key\. . .IP "" 0 . .P Keywords can be repeated within one email at will\. Letter case doesn’t matter\. . .P There are two types of keywords: those to enhance messages sent over the list (“list\-keywords”), and those to request something from Schleuder (“request\-keywords”)\. . .P Find detailed descriptions of all available keywords in the Schleuder documentation online (\fISEE ALSO\fR)\. . .SS "Contact list\-owner" Write to \fIlistname\-owner@hostname\fR to contact the list\-owner(s) even if you don\'t know who they are\. Use the list\'s key to encrypt the email! . .SH "EXIT STATUS" . .TP 0 Incoming email was processed without errors\. . .TP 1 Internal failure in incoming email processing\. . .SH "FILES" . .IP "\(bu" 4 \fB/etc/schleuder/schleuder\.yml\fR: default path of global Schleuder configuration . .IP "\(bu" 4 \fB/etc/schleuder/list\-defaults\.yml\fR: default path of default list settings . .IP "\(bu" 4 \fB/var/lib/schleuder/lists\fR default path of lists_dir . .IP "\(bu" 4 \fB\fR/\fB\fR/`\fIlistname\fR: list internal data . .IP "\(bu" 4 \fB\fR/\fB\fR/\fB\fR/list\.log`: log\-file for list . .IP "" 0 . .P All configuration files are formatted as YAML\. See \fIhttp://www\.yaml\.org/\fR for more details\. . .SH "BUGS" Known bugs are listed on the Schleuder bugtracker at \fIhttps://0xacab\.org/schleuder/schleuder\fR . .SH "SEE ALSO" \fBschleuder\-cli\fR(8), \fBgnupg\fR(7)\. . .TP Website of \fBschleuder\fR \fIhttps://schleuder\.org/\fR . .TP More extensive documentation for \fBschleuder\fR \fIhttps://schleuder\.org/docs/\fR . .TP \fBschleuder\-cli\fR, the command line interface for list\-management \fIhttps://0xacab\.org/schleuder/schleuder\-cli/\fR . .TP \fBschleuder\-web\fR, the web interface for list\-management \fIhttps://0xacab\.org/schleuder/schleuder\-web/\fR schleuder-5.0.1/man/schleuder.8.ron000066400000000000000000000131771502127241700171240ustar00rootroot00000000000000schleuder(8) -- an email hub for groups ======================================= ## SYNOPSIS `schleuder` work < `schleuder` help `schleuder` [<...>] For descriptions of the other commands see the output of the -command. This manual page is written for system administrators. For other information please read the Schleuder documentation online ([SEE ALSO][]). ## DESCRIPTION Schleuder is an email hub for groups. Subscribers can communicate encryptedly and pseudonymously among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list. Schleuder takes care of all de- and encryption, stripping of headers, formatting conversions, etc. Further Schleuder can send out its own public key and receive administrative commands by email. Email cryptography is handled by using GnuPG. ## ENVIRONMENT ### Configuration Schleuder reads its basic settings from a file that it by default expects at . To make Schleuder read a different file set the environment variable to the path to your file when running schleuder. E.g.: SCHLEUDER_CONFIG=/usr/local/etc/schleuder.yml /path/to/bin/schleuder ... For explanations of the possible settings read the default config file. The default settings for new lists are read from another config file. By default Schleuder looks at . To make Schleuder read a different file set the environment variable analogous to above. The possible settings are explained in the default config file. ### Connect to MTA Schleuder behaves like an email-filter: it reads email from standard-input, and reports errors to standard-error. If all goes well Schleuder closes the initial connection to the Mail Transport Agent (MTA) only after it sent out all outgoing emails. In case of an error the MTA is expected to include Schleuder's error message into a bounce-email that is sent back to the sender (this is default behaviour of most MTAs). To connect the MTA with Schleuder it must pipe the incoming message into Schleuder's STDIN, and give it two arguments: first one: "work", second one: the full recipients email address. For more information on how to integrate Schleuder with your existing mail setup, please read the Schleuder documentation online ([SEE ALSO][]). ### Data storage The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under /// ( is read from schleuder.yml, by default it is ). They can be used manually using gpg2. Please be careful to maintain proper file permissions if you touch the files. In the list-directory there’s also a list specific log-file (might be missing if the log-level is high and no error occurred yet). Other logging is sent to syslog. Where that ends up depends on the operating system and the system administration. All other list-related data is stored in the SQL-database. Most data is unserialized, only some values are JSON-encoded. ## SPECIAL FEATURES Schleuder features some special functionality. For more detailed information read the Schleuder documentation online ([SEE ALSO][]). ### Getting the public key of a list Each Schleuder-list replies with its public key to any email sent to . E.g. to receive the key for the contact address of the Schleuder project write an email to
. ### Email commands Schleuder knows some special keywords that trigger different behaviour. You can e.g. subscribe someone, or resend an email to a non-subscriber using keywords. Keywords require that: * they start the line and begin with "x-", * they are written into the beginning of the *first text-part* of the email (usually that’s just the normal body of the email), * possible arguments must be written *on the same line* as the keyword (exceptions are mentioned in the descriptions below), * the email must be *encrypted and signed* by a list-member’s key. Keywords can be repeated within one email at will. Letter case doesn’t matter. There are two types of keywords: those to enhance messages sent over the list (“list-keywords”), and those to request something from Schleuder (“request-keywords”). Find detailed descriptions of all available keywords in the Schleuder documentation online ([SEE ALSO][]). ### Contact list-owner Write to to contact the list-owner(s) even if you don't know who they are. Use the list's key to encrypt the email! ## EXIT STATUS * 0: Incoming email was processed without errors. * 1: Internal failure in incoming email processing. ## FILES * `/etc/schleuder/schleuder.yml`: default path of global Schleuder configuration * `/etc/schleuder/list-defaults.yml`: default path of default list settings * `/var/lib/schleuder/lists` default path of lists_dir * ``/``/`: list internal data * ``/``/``/list.log`: log-file for list All configuration files are formatted as YAML. See for more details. ## BUGS Known bugs are listed on the Schleuder bugtracker at ## SEE ALSO `schleuder-cli`(8), `gnupg`(7). * Website of `schleuder`: * More extensive documentation for `schleuder`: * `schleuder-cli`, the command line interface for list-management: * `schleuder-web`, the web interface for list-management: schleuder-5.0.1/schleuder.gemspec000066400000000000000000000055131502127241700170230ustar00rootroot00000000000000# encoding: utf-8 $: << File.expand_path('../lib', __FILE__) require 'schleuder/version' Gem::Specification.new do |s| s.name = 'schleuder' s.version = Schleuder::VERSION s.authors = 'schleuder dev team' s.email = 'team@schleuder.org' s.homepage = 'https://schleuder.org/' s.summary = 'Schleuder is an encrypting mailing list manager with remailing-capabilities.' s.description = "Schleuder is a group's email-gateway: subscribers can exchange encrypted emails among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list.\n\n(Please note: For some platforms there's a better way of installing Schleuder than `gem install`. See for details.)" s.files = `git ls-files lib locales etc db README.md Rakefile`.split s.executables = %w[schleuder schleuder-api-daemon] s.platform = Gem::Platform::RUBY s.require_path = 'lib' s.license = 'GPL-3.0' s.metadata = { 'homepage_uri' => 'https://schleuder.org/', 'documentation_uri' => 'https://schleuder.org/docs/', 'changelog_uri' => 'https://0xacab.org/schleuder/schleuder/blob/main/CHANGELOG.md', 'source_code_uri' => 'https://0xacab.org/schleuder/schleuder/', 'bug_tracker_uri' => 'https://0xacab.org/schleuder/schleuder/issues', 'mailing_list_uri' => 'https://lists.nadir.org/mailman/listinfo/schleuder-announce/', } s.required_ruby_version = '>= 3.1.0' s.add_runtime_dependency 'activerecord', '~> 7.1' s.add_runtime_dependency 'bcrypt', '~> 3.1.2' s.add_runtime_dependency 'charlock_holmes', '~> 0.7.6' s.add_runtime_dependency 'gpgme', '~> 2.0', '>= 2.0.19' # Explicitly include to force a version. s.add_runtime_dependency 'mail', '~> 2.8.1' s.add_runtime_dependency 'net-smtp', '~> 0.3.1' s.add_runtime_dependency 'rake', '>= 10.5.0' s.add_runtime_dependency 'sinatra', '~> 3.1.0' s.add_runtime_dependency 'sinatra-contrib', '~> 3.1.0' s.add_runtime_dependency 'sqlite3', '~> 1.6.0' s.add_runtime_dependency 'thin', '~> 1' s.add_runtime_dependency 'thor', '~> 1.3' s.add_runtime_dependency 'typhoeus', '~> 1.4' s.add_development_dependency 'byebug', '~> 11' s.add_development_dependency 'database_cleaner', '~> 2.0' s.add_development_dependency 'factory_bot', '~> 6.0' s.add_development_dependency 'hirb', '~> 0' s.add_development_dependency 'irb' s.add_development_dependency 'rack-test', '~> 1' s.add_development_dependency 'rspec', '~> 3' s.add_development_dependency 'rubocop', '~> 1' s.add_development_dependency 'simplecov-console', '~> 0' s.post_install_message = " Please consider additionally installing schleuder-cli (allows to configure lists from the command line). To set up Schleuder on this system please run `schleuder install`. " end schleuder-5.0.1/spec/000077500000000000000000000000001502127241700144265ustar00rootroot00000000000000schleuder-5.0.1/spec/custom_keyword_handlers/000077500000000000000000000000001502127241700213645ustar00rootroot00000000000000schleuder-5.0.1/spec/custom_keyword_handlers/custom_keyword.rb000066400000000000000000000002721502127241700247700ustar00rootroot00000000000000class CustomKeyword < Schleuder::KeywordHandlers::Base handles_request_keyword 'custom-keyword', with_method: :custom_keyword def custom_keyword 'Something something' end end schleuder-5.0.1/spec/factories/000077500000000000000000000000001502127241700164055ustar00rootroot00000000000000schleuder-5.0.1/spec/factories/lists.rb000066400000000000000000000031671502127241700200770ustar00rootroot00000000000000FactoryBot.define do factory :list do sequence(:email) {|n| "list#{n}@example.org" } fingerprint { '59C71FB38AEE22E091C78259D06350440F759BD3' } log_level { 'warn' } subject_prefix { nil } subject_prefix_in { nil } subject_prefix_out { nil } openpgp_header_preference { 'signencrypt' } internal_footer { nil } public_footer { nil } headers_to_meta { ['from', 'to', 'cc', 'date', 'sig', 'enc'] } bounces_drop_on_headers { { 'x-spam-flag' => true } } keywords_admin_only { ['subscribe', 'unsubscribe', 'delete-key'] } keywords_admin_notify { ['add-key'] } send_encrypted_only { true } receive_encrypted_only { false } receive_signed_only { false } receive_authenticated_only { false } receive_from_subscribed_emailaddresses_only { false } receive_admin_only { false } keep_msgid { true } bounces_drop_all { false } bounces_notify_admins { true } deliver_selfsent { true } include_autocrypt_header { true } include_list_headers { true } include_openpgp_header { true } max_message_size_kb { 10240 } language { 'en' } forward_all_incoming_to_admins { false } key_auto_import_from_email { false } logfiles_to_keep { 2 } after(:build) do |list| FileUtils.mkdir_p(list.listdir) gpghome_upstream = File.join 'spec', 'gnupg' FileUtils.cp_r Dir["#{gpghome_upstream}/{private*,*.gpg,.*migrated}"], list.listdir end trait :with_one_subscription do after(:build) do |list| create(:subscription) end end factory :list_with_one_subscription, traits: [:with_one_subscription] end end schleuder-5.0.1/spec/factories/subscriptions.rb000066400000000000000000000003561502127241700216450ustar00rootroot00000000000000FactoryBot.define do factory :subscription do list sequence(:email) {|n| "subscription#{n}@example.org" } fingerprint { '129A74AD5317457F9E502844A39C61B32003A8D8' } admin { true } delivery_enabled { true } end end schleuder-5.0.1/spec/fixtures/000077500000000000000000000000001502127241700162775ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/bla_foo_key.txt000066400000000000000000000031741502127241700213160ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBEudENYBCADPG94KbqEUQSv3yKbn7Oh/ky0Wn0QTgeTAB/T+oP5au9I/5CnS /Rgd8M4k4n/g9orPDfZ1kp3G0sMphLs5XFh9rdtk4iZUVDdU20nfB1lHGMZreGfv mhWyYs7GlitFPHGhJUSdQ6kmxR5MjnfE8S+nXYVWkthHxaU21NIkXGyGWcTCc4ML 8BbJAsgZt2QCWE+l4OO04GoLJtttug8a2RqAuzGHit2+yc8Zv9HAwUjexrw+KZhI TnTOiT4aF5XZmVJyPYAaksjKtAXbkR7nWDWi4yTTm6VFEN6Jpajk3CEqBuyFJW+Y +60oXjf8ktwughxiV5IJCljlDoX1BDPJXw7XABEBAAG0EGJsYWJsYSA8YmxhQGZv bz6JATgEEwECACIFAkudENYCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJ EOvb6JklHyQSklQH/iavy1ercHoF7VI71b9iSrRHvlYDEnwED9y3p2ZNtOiR3dBX 6/vFErWoLnaIRSXfsUMnMtJxuxIwu0xCXmGxzzXqX1HFjZk6ofG7eajL/JC+ugAg 0ZNsHHBrBfgaWypyO7QSZyZDlEog7Yk6/Dji1fv0RZsqKo4kF+Fc5HsUyw7yN22K yjCjdu+KCgnMA6D1GLG++AirWnWxQW1CCqykuzXIMy2Z040zu8q9hv/m4m+0IXuS eFCkcrN2taP3bY+Ynn8r2X6ZV9xhMOJ13ylqn7hCebH1QsGxfIN2mfFesrUbuLb4 9KSr0qQBkYG81xOfBemCAJNu5pzQBGebgp9siX65AQ0ES50Q1gEIAMx0UKr+H63b XRhB+R/Ipn6njz3YnEAUhMYUgZueeh5MRwf1CJXXPdJ0avWRdbD689T0qhuwHMw0 iAebRxPOTzdbaUiNzXlS+9kfJu/AEbRFJPo1MgiDJr73lLpMWHUxWHSRC/OSoSYA /AHBriTorxaWpmc7A1rfdC13sVPq87TRX8MLhRXOlEiryD+AeM4rmWvBWZiljPuT Oa7dQBkO5USYkkiamlkfHaBGEfFdiWVBP6XIFiixFDUL1BKsImhpyht59aLb4Bar hIxft17xPmoDNVOGj3k0/xkFJEv6N7btB8I5DfcjdOZ3CYd8Orprm+dZPajnZeaS 8sde5ngjTZcAEQEAAYkBHwQYAQIACQUCS50Q1gIbDAAKCRDr2+iZJR8kEizPB/4x xcvm29isTIoVEtEXNkzs589YHarVKNnB1rbBRyB+p/mhdhjb7zVSIPrGrwss0kzC i00sHYr3a70MbKfM8fAoFr2d9ikoUVfBl1tVxCNH+6FYxp8g1vcNmcTMfsArFnFi 2MQOrnJNkUatAuprAouOLEApiGgTy6QikVpsxYPUT+BXHTP/vKW+fg/rJq87ub2v IuhwdI04saTRDj7jYzQsTWDVmwaTd3zcov8H9EL7DGUudZxi3t4kFfOwqBzbQ/fH fOGFBWOe1xM7EmdS0Gplss1pAnY+MuSicD0/j7/tWn7wJelMuTXxczSHM+4Vs/Yw 86SvVJhKa3G/1UCnzzzp =trkO -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/broken_utf8_uid_key.txt000066400000000000000000000032101502127241700227730ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQGiBD4tUP0RBADyrM6g52fnEgXryP54M2R+2AXcyXo6qEd7ZXzqoh9myckJ3gLS DsS7Og/TOev8w3h5ZHmFvDgWBZjzcqg0DGC17pSyBvOx8Byhz6Wd6U7YibxjpBuS /N+WMm5xEjlJVV1SJvNzMSxD5rVOprA61DgfsbW6ScAMk3VDW7jxLcG05wCg/ynJ j/vdowhlqjPJIlSZK49mQfcD/0IhBgd4M4SxxUWQNUdhm7GMXuC2pxqhqnKoSMM7 zmndGPOlf29Xn7Zc3mW8LUvaz2HmJyztMqk0F3qfNTPXEUu8OvyheN5vFNwysF2O zE8wSv8LcyH4GVxwNUSSy3dxcKfJfIoLOpmJ7TZjJ5rkPkYg5UpOlTohAW8EhPSW KU+RA/sH/h8wVdQclBJ3IxJylZ0sk2OyEbaobmGtJbch8Yqo/cbYhprmDKgE6L3h e4cKan78NTr+g7q4O+enHkkql7PW91aormHpCIDd8q01wcbvcdnjsgS4adESq/Eb p8ZZj5itaOslJWvqvyi4j2X0c1hXXHG0MxMvyOIHPHgU4krnd7Q5QvxuZG5pcyBB bGxlIGdlZ2VuIFJlY2h0cyA8aW5mb0BidWVuZG5pcy1nZWdlbi1yZWNodHMuY2g+ iFgEEBECABgFAj4tUP0ICwMJCAcCAQoCGQEFGwMAAAAACgkQEkL24T2OvkqbBwCg wQ2rPbHxPqVU36HTIOr9pKbU5YgAmgK4sDjlS7naIy7Yg6u0q9UcNRtSuQINBD4t UP0QCAD2Qle3CH8IF3KiutapQvMF6PlTETlPtvFuuUs4INoBp1ajFOmPQFXz0AfG y0OplK33TGSGSfgMg71l6RfUodNQ+PVZX9x2Uk89PY3bzpnhV5JZzf24rnRPxfx2 vIPFRzBhznzJZv8V+bv9kV7HAarTW56NoKVyOtQa8L9GAFgr5fSI/VhOSdvNILSd 5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsYjY67VYy4XTjTNP18F1dDox0Y bN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak XUGfnHy9iUsiGSa6q6Jew1XpMgs7AAICB/95aHF6jqhYsbsyFGm7YKeSWFUjy1Hh LJMnrfsY043xZqfwiXyDeNT5xAInN6YibUFIU+xCu5zdtknEfUiI1/nMK3pWkYgQ 3xwyJPTpboidcLucinYQejz4Nx14p5/Lv/jpmYlAesjy/t/QINXzDn/BeoOwyOtY 8AxL++X0M95UcNOqvWaXthTBtsGYAxoCvpzmxaHagqw90FYvjGHL5PymCCOHpZZ3 GfL10lc16nUFvetqhZTP3+hG6I5VQi8sHvcULgodLHSM2gkfu8gvcsYbyNZNRKrS WqLC0/pPhkQGNGPLC3uUlBC2tGMS1iWiIOODiDl3brW61FmEBGP1qFl9iEwEGBEC AAwFAj4tUP0FGwwAAAAACgkQEkL24T2OvkorgQCgkMFb92w+01OAABwrjGCIW97h W0UAn3D/PPbXYP0uo3sDOePOqeOv0ls7 =Q88o -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/default_list_key.txt000066400000000000000000000060121502127241700223660ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 /J2/tBYro55u =lep5 -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/example_key.txt000066400000000000000000000060441502127241700213470ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG/MacGPG2 v2 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 gpCMrbUUdX4= =H2Gw -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/example_key_binary.pgp000066400000000000000000000042501502127241700226570ustar00rootroot00000000000000 XN 璀y~?^yDx9 ~a-S aw"H Dq}( *XF n ;QjCeOLj4$rL ى5R?UB }G9KF1wH|r)1D#tv?{alzBaza6?گ_eʓbc)q;_okFStTt"gAp*0maXucvjm}3P|4ͫm)Another Testuser 8"XN   a:zd 9~l^|55/J';,s V~YAɧ^zh{C %~okIY\?Xtngb#]J%D+#^<D,Nف LnBGMr2;q {vJk؅un澹` kIWu1Iޫx|Ve8Jܪ2PY[bzޑG)>x+ʹg!foFX` "VIݝNXE~b~>"%r1adZTr1]d"(RQϟ&x~<,;#EMnhP\7QAJ҂V̵TS=-id?2'v z+}~QM"*;Du"wa_p&՟eε,!nReЋ|=VVMlȻl^+GfC`gԺP4bxB޾N~ a5w{ ]G-r XC{ 9 bY)b/@h-s#.8.sFeq 6R BPf8?)3:޲|: x,;zM1+bZRU] GKG6U@.ZWǑ=p\ XN a:-z_3&Q)>b%eYJœIn`}wVXLܳ5puIVHZL,EYc6WNgyCRQ;q<`*ٓ$<3exn&LB/t3'ԉmbt5+jTZ[o3\* BeGy].4,}R㸂!_s&@ .@~T|DQevRy>"~1ʫ'LYׂ;۫Y dkOUzhdp|Lf&cH͍xIY_/wݔHf-V26CP2И\ "_K} MaJL*`BC?glԈOP1\sC&mwuD1\_k`8|ЏJv%+yK@!'&YנK{52G 1̛h@wWj)5nu~schleuder-5.0.1/spec/fixtures/expired_key.txt000066400000000000000000000032201502127241700213450ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBExlilkBCACb2AQyclf7latAIE1kCTfKQ9jmcKyf959ymyhzoeNmBDpKjILC 7MOXtICo/V/xAzhWBK/vT9+56brGUBTugnW3yK+zllQprI3kIYaRS1SrbmKVwVse 9qLVUL1BssohFaEeQqT4MNh62ziJymqCguGEGXpYlEqzEDTmmhTANiPKRBZDrdfq 3FU3OJUMTGzuG34mKmXMRr0azprF228LUujMMKyWhG1hxh3El04C4jPuMSbaVcwN E2rgIg8jaNAQuSyXkaprPZ8/nRG8UFGRtCMEIEh6Ou6KybF1NI9LQKCwcsGcLHKU 2u/8vOCExxdwl9Jjlqmof4FQV7bT++6SC0n3ABEBAAG0EWV4cGlyZWQgPGJsYUBm b28+iQE+BBMBAgAoBQJMZYpZAhsDBQkAAVGABgsJCAcDAgYVCAIJCgsEFgIDAQIe AQIXgAAKCRD3Gj+EEtg4iV6yB/4uDLoN1+TswtGjpUlu2CyjHe7pb05dAU4sWfTV I+fxBuyEo+cf/23nOeoGyltBDR0heSg3TIfXQrbWD4WoVsOXPaT0fq6UEzeadkmn A5NN3PGkv46o3ZSF1ltkY9ybMgnmRLHYCojSu5bSBMRVyurr0ozwNRPtFUTka8Lj wxiwDJ394D5y6PjL56FPkUdKydzFGV2ptSKsqyAJvMBeGlQ4I6TpiBx0Lz2A1Qn+ 4uXgTVPqgalC5YKTTTjOfQcieOOeqtI0LHqDpS/DIPLnwTUCN8OL2TQIeDudm3YI P8FCKvImh840vTbpgFSgQeaJzJFv9UrloNyyvbVtaeoxnxoBuQENBExlilkBCADD Gbf0TEs5HDpbUC78tJetGWipmQRTq8gS9dDoKjG5mvlpFARPTAJvewgI7DICXMtV Y8P8eFbsZDGMETduunadutDvyP9J/wuknYHJkE5jJeZjEKyofrWM1BHxHb2bkNU0 hr8EVNvEhVjAb62cJPj5emi/3UhynLoJrrO+AZSr9Bq2QW38ntSUxTXM7VbGUR1M WZSoJ+gEg/IeR2HJziCyb1mhlvo9pZqJT959bNVQ/xU4NHtYca5cV3X88Ald4Dwz 2HM+PIBbsiz3C3fIMNrRxRvMCU4PgsjSZFoRxcdDRT5OjBxNgQ4UmWQH0sTFw9Tx 8B2OtwuguGwaXIV3FpRDABEBAAGJASUEGAECAA8FAkxlilkCGwwFCQABUYAACgkQ 9xo/hBLYOImqngf/QQt0S6tlJ9OMknmAr2pNg+DQkCqTNaSk/iQj4leGGwkpRVoH 5VFTZ0nkmZjcDTTrCjj5rEDaRo8Q38KsB1po8P25ABoK0b28rHw8I3L2Byl1+IB7 +dNKVyFJVfHAYOQsbI/p/2KdtZZIbpxnRVHY+Vlu2p/fx3JqPmqCiaVMcUFw55Qj SKaI+omfnN0WGyrK1Rub925Lch0vZkxVwmTse7qufg0iwTREMy9VZfMavMhkAtM2 AsiEG8j3mwU9JQfBkXqtWxG2VvtsBJ0rLafh5sR3NgjtR2vbSdzJRCV2xO4z6Drh 3Pug3ReHmcLUFTDPE+vmeH+xpEZ2nhvNRFDhmQ== =Nao0 -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/expired_key_extended.txt000066400000000000000000000033421502127241700232320ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: SKS 1.1.6+ Comment: Hostname: sks.spodhuis.org mQENBExlilkBCACb2AQyclf7latAIE1kCTfKQ9jmcKyf959ymyhzoeNmBDpKjILC7MOXtICo /V/xAzhWBK/vT9+56brGUBTugnW3yK+zllQprI3kIYaRS1SrbmKVwVse9qLVUL1BssohFaEe QqT4MNh62ziJymqCguGEGXpYlEqzEDTmmhTANiPKRBZDrdfq3FU3OJUMTGzuG34mKmXMRr0a zprF228LUujMMKyWhG1hxh3El04C4jPuMSbaVcwNE2rgIg8jaNAQuSyXkaprPZ8/nRG8UFGR tCMEIEh6Ou6KybF1NI9LQKCwcsGcLHKU2u/8vOCExxdwl9Jjlqmof4FQV7bT++6SC0n3ABEB AAG0EWV4cGlyZWQgPGJsYUBmb28+iQFVBBMBAgA/AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIe AQIXgBYhBJh2nooQkfNr2IQD7PcaP4QS2DiJBQJYgOG1BQkMHGNXAAoJEPcaP4QS2DiJxiUH /2xYCT+mldoaWMyJlBleSjx0wtWeGNayMuv0RjU2pIkBmslYp/ZZkt+JC3thpJneBW5pJuRB qeUi7yTigdtOrH47g+FfIDCY89ymTDbYW4vpNnnsV7s+ke8tbEmTtMpjFypoTvbnGYlq8VLz 87eRcsLwADOAJfFBdnDD0tyNCrUY/V+Ti/ZI4bHoFA14t8Hm7MIDkfB6sVfzpnZd1ACj+klv 1rfq+9m56lsavS/dM+BlhwfRORT9cenuBs++AXXWvh1CZW/J06kFECG+ptqU5246nQcjE5GX W8sC+TSq7OXSTQAJDF+aWqjA/JrbpSf/3r2/IU+mGH2Bwi7B5uBN6lG5AQ0ETGWKWQEIAMMZ t/RMSzkcOltQLvy0l60ZaKmZBFOryBL10OgqMbma+WkUBE9MAm97CAjsMgJcy1Vjw/x4Vuxk MYwRN266dp260O/I/0n/C6SdgcmQTmMl5mMQrKh+tYzUEfEdvZuQ1TSGvwRU28SFWMBvrZwk +Pl6aL/dSHKcugmus74BlKv0GrZBbfye1JTFNcztVsZRHUxZlKgn6ASD8h5HYcnOILJvWaGW +j2lmolP3n1s1VD/FTg0e1hxrlxXdfzwCV3gPDPYcz48gFuyLPcLd8gw2tHFG8wJTg+CyNJk WhHFx0NFPk6MHE2BDhSZZAfSxMXD1PHwHY63C6C4bBpchXcWlEMAEQEAAYkBJQQYAQIADwUC TGWKWQIbDAUJAAFRgAAKCRD3Gj+EEtg4iaqeB/9BC3RLq2Un04ySeYCvak2D4NCQKpM1pKT+ JCPiV4YbCSlFWgflUVNnSeSZmNwNNOsKOPmsQNpGjxDfwqwHWmjw/bkAGgrRvbysfDwjcvYH KXX4gHv500pXIUlV8cBg5Cxsj+n/Yp21lkhunGdFUdj5WW7an9/Hcmo+aoKJpUxxQXDnlCNI poj6iZ+c3RYbKsrVG5v3bktyHS9mTFXCZOx7uq5+DSLBNEQzL1Vl8xq8yGQC0zYCyIQbyPeb BT0lB8GReq1bEbZW+2wEnSstp+HmxHc2CO1Ha9tJ3MlEJXbE7jPoOuHc+6DdF4eZwtQVMM8T 6+Z4f7GkRnaeG81EUOGZ =4p8E -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/expiring_key.txt000066400000000000000000000033511502127241700215370ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFmCxIEBCADUiLMwu7I/EuyyNlidrllyVt2GkXVwSa1yIR3nA1KyDqizspih SL4eVaZOvVWyJaGxIFbmG5WHCQi0YuuDv0jcGYT3lmyPumrDYyaU5GHm05gmZ+R4 +oSjJG/v5z7L6au2G/+Iw5pQg819VviJ6po+QwmQeUkHd7xhiniPq7aoVFcoltgE bQF8sPWJ1jyFnTmL36MECgAP9MKPfcSuHzUWJxiDSo8Siqaf7uUY7F2Gz9pBYuiv g6PJqnagtjZGPgxKqQak/IRvPjk6WWIGpaO9fBl1GQ/W3iuhkObBWuHNdErygQX8 0VDc/wRfIeFsMjaPQ/nQOALgzsTGZWpdO/T9ABEBAAG0JmV4cGlyaW5nIGtleSA8 ZXhwaXJpbmdrZXlAZXhhbXBsZS5vcmc+iQFUBBMBCAA+FiEEQh+/cZBkATZ4hZPN num+WSnKzCAFAlmCxIECGwMFCSWYBgAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA CgkQnum+WSnKzCBnQwgAmCy016/VxvLHSZ45TuYO60dGfrmu6nA3/F2qKjXsszlw UL+7jiFZZzLRQF8GJGnHJnhg6mCz8yQKam8pOqaYCqVvc19GyzPdQ22uiOA0QcuE 0kHd/ncAEtRJ3q+yeb7rcxVlHDHx2VpzNY3d2aRsgSYin+iAWKROUt3efZyTquzJ etOl6SvbwTv+qqDmrEj/zNQmr1Dic+KtLvK8ClyXrfZou5wCGsX14fKk7xkhNPBS f2oCaPjMjGsh9gZ+nZfpv+ppTAbZRPSKHuQrWksFGGxDTMigQORNtwA2SBljmL63 neqajK086P3U3dZm4lD4dPCG/V2VVYgLsooCzJZUYbkBDQRZgsSBAQgAx9bBVzuv AlLbPIqHyXsjurosvoolUKAjdWFaGCK1lZHdy3RwuVVdgWA7qXpr/T6sQ0OzxaK/ HT/1G0haCKd3zC2dD3FUQJHzIPXqE4x36a+PF/qq2vVe43+8Lwz2ceopHprHP7Sx anqnagiV/JQNJna7WTk4RM/61oHiBNWyusEhW2vynTs92ltEL4Esh3BKpyQ6mmV+ DLGalraMSDcuGRzwQMTlRrXygxq2WHc9px3M3PfMBSj86ouhSOkwOPI3eV4XPzB+ uXxwcZTsUxPm8BVVt5OAmrLaaX2K/AWEsZlVdSJpZrFD2WMMo3YRKrwvM8AHhuQO FJKaHPibi+a3zwARAQABiQE8BBgBCAAmFiEEQh+/cZBkATZ4hZPNnum+WSnKzCAF AlmCxIECGwwFCSWYBgAACgkQnum+WSnKzCAwMAf+L5ZFRTgha0AKnmWTR7JA2weg EPPDt33AHZFOCYcxuHm8TPgl4OuSvoxxWovCm56/nIiWnqojNVlWCQxAHHQmC1dA xJ7EDqdqCqVvRyRCzUsONICKeHdAoNA7TWw/DF+JZn2l+ud2w+EiQt9qy3sQXZeG SOG6s5kSPxxuwPmmMmCu5u+QI4elcHaw9RLGFbwlO4VlBS/n0k6P+UDY/4Eqkyi0 L7GCFtw3ZKjRR24MKYdGRbv7hpzvp6JyrbYA9H4wGJir43Dk/EH7Ul+4Y8FcHFk4 FU9yPzi0dW07tKD29F0GxRdqtIs0rWHIbUJVskDwDhJdteIsO/bTuKN4tZvtcQ== =c/Mw -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/filters/000077500000000000000000000000001502127241700177475ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/filters/post_decryption/000077500000000000000000000000001502127241700231745ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/filters/post_decryption/75_post_example.rb000066400000000000000000000001111502127241700265250ustar00rootroot00000000000000module Schleuder::Filters def self.post_example(list, mail) end end schleuder-5.0.1/spec/fixtures/filters/pre_decryption/000077500000000000000000000000001502127241700227755ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/filters/pre_decryption/25_example.rb000066400000000000000000000001041502127241700252560ustar00rootroot00000000000000module Schleuder::Filters def self.example(list, mail) end end schleuder-5.0.1/spec/fixtures/filters/pre_decryption/invalid_filter_file.rb000066400000000000000000000001111502127241700273050ustar00rootroot00000000000000module Schleuder::Filters def self.never_show_up(list, mail) end end schleuder-5.0.1/spec/fixtures/filters_without_pre/000077500000000000000000000000001502127241700224005ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/filters_without_pre/post_decryption/000077500000000000000000000000001502127241700256255ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/filters_without_pre/post_decryption/05_post_example.rb000066400000000000000000000001101502127241700311460ustar00rootroot00000000000000module Schleuder::Filters def self.post_example(list, mail) end end schleuder-5.0.1/spec/fixtures/mails/000077500000000000000000000000001502127241700174045ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/attach-list-key-thunderbird.eml000066400000000000000000000061621502127241700254230ustar00rootroot00000000000000From: user To: testlist@example.net Message-ID: <6ce92946-85f0-075a-3577-ab5f6420d389@example.net> Date: Sat, 4 Jan 2020 19:43:54 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.3.1 MIME-Version: 1.0 Subject: ... Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="1JxyxqJLgO8VkVyQcZm0f2mqBg2sGa75c" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --1JxyxqJLgO8VkVyQcZm0f2mqBg2sGa75c Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --1JxyxqJLgO8VkVyQcZm0f2mqBg2sGa75c Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 YnGV/JagBA== =sqcR -----END PGP MESSAGE----- --1JxyxqJLgO8VkVyQcZm0f2mqBg2sGa75c-- schleuder-5.0.1/spec/fixtures/mails/big_first_mime_part.txt000066400000000000000000000643421502127241700241630ustar00rootroot00000000000000Content-Type: multipart/mixed; boundary="NrllqtU3K9aakndDUX4Wq60iXnCpR3rT4"; protected-headers="v1" From: foo To: list1@lists.example.net Message-ID: Subject: My protected subject References: In-Reply-To: --NrllqtU3K9aakndDUX4Wq60iXnCpR3rT4 Content-Type: multipart/alternative; boundary="------------030809040800070509090406" This is a multi-part message in MIME format. --------------030809040800070509090406 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable foobar lg >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> Cc:=20 >>>>>> >>>>>> >>>>>> >>>>>> >> > --------------030809040800070509090406 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable foorbar
lg

Am 23.11.2017 um 11:51 schrieb foobar@lists.example.net:



Am 23.11.2017 um 09:44 schrieb foobar@li= sts.example.net:
=
From: bbaauunn <bbaauunn@=
example.com>

To: foobar@lists.example.net

Cc:=20

true" class=3D"moz-txt-link-rfc2396E" href=3D"mailto:bbaauunn@example.com"=
><bbaauunn@example.com>

Enc: Encrypted



hey,






Am 22.11.2017 um 19:03 schrieb <= a class=3D"moz-txt-link-abbreviated" href=3D"mailto:foobar@lists.example.net" moz-do-not-send=3D"true">foobar@lists.example.net= :
From: bbaauunn <utop=
ia11@example.com>







To: foobar@lists.example.net







Cc:=20














true" class=3D"moz-txt-link-rfc2396E" href=3D"mailto:bbaauunn@example.com"=
><bbaauunn@example.com>







Enc: Encrypted









br>

To: foobar@lists.example.net Cc:=20 Enc: Encrypted

































































































































































From: bbaauunn <=
bbaauunn@example.com>
















              

              
Cc:=20
















              
















              

              
Enc: Encrypted
















              

              

              

              

              

              

              
hey!
















              
















              
















              
















              
















              
















              
lg
















              

              

              

              

              

              

              
To: 
















              
Cc:=20
















              
















              

              
Enc: Encrypted
















              

                

              

                

              

                

              

                

              
















              

                

              
















              

                

              
















              
















              
















              

                

              

              

              

              
Cc:=20
















              
















              

              
Enc: Encrypted
















              

                  

              

                  

              

                  

              
















              
















              
















              

                  

              

                  

              

              

              

              
Cc:=20
















              
0
















              

              
Enc: Encrypted
















              

                    

              

                    

              

                    

              

                    

              
















              
















              
















              

                    

              
















              
















              
















              

                    

              
















              

                    

              
LG
















              

                  

              

              

              

              

            





--------------030809040800070509090406-- --NrllqtU3K9aakndDUX4Wq60iXnCpR3rT4-- schleuder-5.0.1/spec/fixtures/mails/bounce.eml000066400000000000000000000114501502127241700213570ustar00rootroot00000000000000Received: by schleuder.example.org with local (Exim 4.87) id 1dOBwu-0005qF-LI for schleuder@example.org; Fri, 23 Jun 2017 00:00:00 +0200 X-Failed-Recipients: paz@nadir.org Auto-Submitted: auto-replied From: Mail Delivery System To: schleuder@example.org Content-Type: multipart/report; report-type=delivery-status; boundary=1498175744-eximdsn-1404350378 MIME-Version: 1.0 Subject: Mail delivery failed: returning message to sender Message-Id: Date: Fri, 23 Jun 2017 01:55:44 +0200 --1498175744-eximdsn-1404350378 Content-type: text/plain; charset=us-ascii This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. The following address(es) failed: paz@nadir.org host 127.0.0.1 [127.0.0.1] SMTP error from remote mail server after RCPT TO:: 450 4.7.1 : Recipient address rejected: Sorry, your message cannot be delivered to that person because their mailbox is full. If you can contact them another way, you may wish to tell them of this problem: retry timeout exceeded --1498175744-eximdsn-1404350378 Content-type: message/delivery-status Reporting-MTA: dns; schleuder.example.org Action: failed Final-Recipient: rfc822;paz@nadir.org Status: 5.0.0 Remote-MTA: dns; 127.0.0.1 Diagnostic-Code: smtp; 450 4.7.1 : Recipient address rejected: Sorry, your message cannot be delivered to that person because their mailbox is full. If you can contact them another way, you may wish to tell them of this problem: retry timeout exceeded --1498175744-eximdsn-1404350378 Content-type: message/rfc822 Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id 1DCDC801DD for ; Thu, 29 Dec 2016 15:09:43 +0100 (CET) From: schleuder@example.org To: paz@nadir.org Subject: bounce test Message-ID: Date: Thu, 29 Dec 2016 15:09:29 +0100 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9 Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9 Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 =eXeI -----END PGP MESSAGE----- --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9-- --1498175744-eximdsn-1404350378-- schleuder-5.0.1/spec/fixtures/mails/bounces/000077500000000000000000000000001502127241700210425ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/bounces/malformed_bounce_01.txt.eml000066400000000000000000000054361502127241700261700ustar00rootroot00000000000000Return-Path: <> Received: by 10.216.28.198 with SMTP id g48cs228780wea; Wed, 16 Dec 2009 02:45:14 +0000 Received: ; Wed, 16 Dec 2009 02:41:20 +0000 Date: Wed, 16 Dec 2009 02:41:20 +0000 From: bounce@rediffmail.com To: support@myswom.com Message-Id: <4b2849b9.a813f30a.6c53.6801SMTPIN_ADDED@mx.google.com> Subject: failure notice Mime-Version: 1.0 Content-Type: multipart/mixed; boundary=mimepart_4b2849cc52ad8_16153fb6b5e769bc586b3 Authentication-Results: mx.google.com; spf=pass (google.com: domain of rediffmail.com designates 119.252.147.126 as permitted sender) smtp.mail= Delivered-To: support@myswom.com Received-Spf: pass (google.com: domain of rediffmail.com designates 119.252.147.126 as permitted sender) client-ip=119.252.147.126; --mimepart_4b2849cc52ad8_16153fb6b5e769bc586b3 Hi. This is the qmail-send program at rediffmail.com. I'm afraid I wasn't able to deliver your message to the following addresses. This is a permanent error; I've given up. Sorry it didn't work out. : - Sorry, no mailbox here by that name. (#5.1.1)Z --- Enclosed is a copy of the message. --mimepart_4b2849cc52ad8_16153fb6b5e769bc586b3 Content-Type: message/rfc822 Return-Path: Received: (qmail 43430 invoked from network); 16 Dec 2009 02:41:20 -0000 X-CNFS-Analysis: score=0 v=1.0 c=1 a=UP0woeyT68oA:10 a=8sBYAydLBaTw9O9PPbvJRQ==:17 a=nOXXgcrqAAAA:8 a=hpnyz_ZHDEhns6bXdMYA:9 a=lOoBsn5VqPa6SSdm1A8A:7 a=n6XhW81lUO9q5u5E7SHj76VjDTMA:4 a=abhB7gKK_5kA:10 a=4phKvQ69a4IA:10 X-REDIFF-SENDER-VERIFY: D=-2, P=D Received: from mail.swom.com (HELO xenon.swom.com) (188.40.35.30) by 0 with SMTP; 16 Dec 2009 02:41:20 -0000 Received: from myswom.com (xenon.swom.com [188.40.35.28]) (Authenticated sender: support@myswom.com) by xenon.swom.com (Postfix) with ESMTPSA id 64B392C60B for ; Wed, 16 Dec 2009 02:43:55 +0000 (GMT) Date: Wed, 16 Dec 2009 02:43:55 +0000 From: Cash Culture To: mithlesh_pm@rediffmail.com Message-Id: <4b28496b5a963_3adb3fc543c451b0910ee@xenon.swom.com.tmail> Subject: Cash Culture added you as a friend on Swom... Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 X-Swom-Uuid: 5c18dfcdfee13aa969f4c0c805e5ed0402b8f49b Cash has joined Swom.com and indicated you are a Friend: I'd like to add you to my wealth network on Swom. We know each other from CashCulture a while ago. - CashCulture To accept or view this invitation from Cash Culture: http://swom.com/c/7113/p/cdf64ba2/r/34007 What is Swom.com? ------------------- Swom.com is the new social networking site for professionals and business owners. It helps you make more money by rapidly expanding your wealth network and increasing your income. It's free to join and it's growing fast. The Swom.com Team --mimepart_4b2849cc52ad8_16153fb6b5e769bc586b3--schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234175799.txt.eml000066400000000000000000000051111502127241700241630ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 12:36:39 2009 Return-Path: <> X-Original-To: bounces-main+agris.ameriks=amerimailzzz.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [213.175.111.87]) by ameriks (Postfix) with ESMTP id 7888A3A1B1 for ; Mon, 9 Feb 2009 12:36:39 +0200 (EET) Received: by Albanis-3.local (Postfix) id 4242E326BA6; Mon, 9 Feb 2009 12:36:39 +0200 (EET) Date: Mon, 9 Feb 2009 12:36:39 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+agris.ameriks=amerimailzzz.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="34F80326BA4.1234175799/Albanis-3.local" Message-Id: <20090209103639.4242E326BA6@Albanis-3.local> This is a MIME-encapsulated message. --34F80326BA4.1234175799/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : Host or domain name not found. Name service error for name=amerimailzzz.lv type=A: Host not found --34F80326BA4.1234175799/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 34F80326BA4 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 12:36:39 +0200 (EET) Final-Recipient: rfc822; agris.ameriks@amerimailzzz.lv Action: failed Status: 5.4.4 Diagnostic-Code: X-Postfix; Host or domain name not found. Name service error for name=amerimailzzz.lv type=A: Host not found --34F80326BA4.1234175799/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 34F80326BA4; Mon, 9 Feb 2009 12:36:39 +0200 (EET) Date: Mon, 9 Feb 2009 12:36:33 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: agris.ameriks@amerimailzzz.lv Subject: subjecting Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209103639.34F80326BA4@Albanis-3.local> Hello from mars! --34F80326BA4.1234175799/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234177688.txt.eml000066400000000000000000000051771502127241700241760ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 13:08:08 2009 Return-Path: <> X-Original-To: bounces-main+aaaaagggrrriiiizz=inbox.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [213.175.111.87]) by ameriks (Postfix) with ESMTP id 904D93A1B1 for ; Mon, 9 Feb 2009 13:08:08 +0200 (EET) Received: by Albanis-3.local (Postfix) id 56B1E326DAB; Mon, 9 Feb 2009 13:08:08 +0200 (EET) Date: Mon, 9 Feb 2009 13:08:08 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+aaaaagggrrriiiizz=inbox.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="313F2326DA9.1234177688/Albanis-3.local" Message-Id: <20090209110808.56B1E326DAB@Albanis-3.local> This is a MIME-encapsulated message. --313F2326DA9.1234177688/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host b.mx.inbox.lv[89.111.3.74] said: 554 5.7.1 : Recipient address rejected: Nonexistent user (in reply to RCPT TO command) --313F2326DA9.1234177688/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 313F2326DA9 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 13:08:07 +0200 (EET) Final-Recipient: rfc822; aaaaagggrrriiiizz@inbox.lv Action: failed Status: 5.7.1 Remote-MTA: dns; b.mx.inbox.lv Diagnostic-Code: smtp; 554 5.7.1 : Recipient address rejected: Nonexistent user --313F2326DA9.1234177688/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 313F2326DA9; Mon, 9 Feb 2009 13:08:07 +0200 (EET) Date: Mon, 9 Feb 2009 13:07:59 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: aaaaagggrrriiiizz@inbox.lv Subject: subjecting Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209110807.313F2326DA9@Albanis-3.local> Hello from mars! --313F2326DA9.1234177688/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234210655.txt.eml000066400000000000000000000114741502127241700241510ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:17:35 2009 Return-Path: <> X-Original-To: bounces-main+this_doesnotexistinAAc=accenture.com@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from amrmr1003.accenture.com (amrmr1003.accenture.com [170.252.248.72]) by ameriks (Postfix) with ESMTPS id D31BD3A08C for ; Mon, 9 Feb 2009 22:17:30 +0200 (EET) Received: from AMRXV1001.dir.svc.accenture.com (amrxv1001.dir.svc.accenture.com [10.10.160.61]) by amrmr1003.accenture.com (8.13.8/8.13.8) with ESMTP id n19KHDRY022245 for ; Mon, 9 Feb 2009 14:17:19 -0600 (CST) Received: from mtahm1100.accenture.com ([10.10.100.240]) by AMRXV1001.dir.svc.accenture.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 9 Feb 2009 14:17:00 -0600 X-BigFish: vps9(zzzzzzz2dh6bh43j68o) X-Spam-TCS-SCL: 7:0 X-MS-Exchange-Organization-Antispam-Report: OrigIP: 87.99.72.11;Service: EHS Date: Mon, 9 Feb 2009 22:15:33 +0200 From: Postmaster@accenture.com Reply-To: ginta.amerika@ameri.lv To: ameriks@amerimail.lv Subject: DELIVERY FAILURE: User this_doesnotexistinAAc (this_doesnotexistinAAc@accenture.com) not listed in public Name & Address Book Mime-Version: 1.0 Message-Id: <20090209201600.E1D7B328DAA@Albanis-3.local> X-OriginalArrivalTime: 09 Feb 2009 20:16:12.0664 (UTC) FILETIME=[40DC5F80:01C98AF3] X-MIMETrack: Itemize by SMTP Server on MTAHM1100/Server/Accenture(Release 5.0.9a |January 7, 2002) at 02/09/2009 02:16:57 PM, Serialize by Router on MTAHM1100/Server/Accenture(Release 5.0.9a |January 7, 2002) at 02/09/2009 02:16:58 PM, Serialize complete at 02/09/2009 02:16:58 PM Content-Type: multipart/report; report-type=delivery-status; boundary="==IFJRGLKFGIR48542UHRUHIHD" --==IFJRGLKFGIR48542UHRUHIHD Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Your message Subject: subjecting was not delivered to: this_doesnotexistinAAc@accenture.com because: User this_doesnotexistinAAc (this_doesnotexistinAAc@accenture.com) not listed in public Name & Address Book --==IFJRGLKFGIR48542UHRUHIHD Content-Type: message/delivery-status Reporting-MTA: dns;mtahm1100.accenture.com Final-Recipient: rfc822;this_doesnotexistinAAc@accenture.com Action: failed Status: 5.1.1 Diagnostic-Code: X-Notes; User this_doesnotexistinAAc (this_doesnotexistinAAc@accenture.com) not listed in public Name & Address Book --==IFJRGLKFGIR48542UHRUHIHD Content-Type: message/rfc822 Received: from AMRXV1002.dir.svc.accenture.com ([10.10.160.62]) by mtahm1100.accenture.com (Lotus Domino Release 5.0.9a) with ESMTP id 2009020914165788:77437 ; Mon, 9 Feb 2009 14:16:57 -0600 Received: from AMRXB1002.dir.svc.accenture.com ([10.10.160.66]) by AMRXV1002.dir.svc.accenture.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 9 Feb 2009 14:16:12 -0600 Received: from amrmr1003.accenture.com ([170.252.248.72]) by AMRXB1002.dir.svc.accenture.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 9 Feb 2009 14:16:12 -0600 Received: from mail110-va3-R.bigfish.com (mail-va3.bigfish.com [216.32.180.113]) by amrmr1003.accenture.com (8.13.8/8.13.8) with ESMTP id n19KG9CS021120 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Mon, 9 Feb 2009 14:16:11 -0600 (CST) Received: from mail110-va3 (localhost.localdomain [127.0.0.1]) by mail110-va3-R.bigfish.com (Postfix) with ESMTP id 4D8DE10008F for ; Mon, 9 Feb 2009 20:16:09 +0000 (UTC) Received: by mail110-va3 (MessageSwitch) id 123421056981910_11174; Mon, 9 Feb 2009 20:16:09 +0000 (UCT) Received: from Albanis-3.local (unknown [87.99.72.11]) by mail110-va3.bigfish.com (Postfix) with ESMTP id E3C59AD0061 for ; Mon, 9 Feb 2009 20:16:08 +0000 (UTC) Received: by Albanis-3.local (Postfix, from userid 501) id E1D7B328DAA; Mon, 9 Feb 2009 22:16:00 +0200 (EET) X-BigFish: vps9(zzzzzzz2dh6bh43j68o) X-Spam-TCS-SCL: 7:0 X-MS-Exchange-Organization-Antispam-Report: OrigIP: 87.99.72.11;Service: EHS Date: Mon, 9 Feb 2009 22:15:33 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: this_doesnotexistinAAc@accenture.com Subject: subjecting Mime-Version: 1.0 Message-Id: <20090209201600.E1D7B328DAA@Albanis-3.local> Return-Path: bounces-main+this_doesnotexistinAAc=accenture.com@amerimail.lv X-OriginalArrivalTime: 09 Feb 2009 20:16:12.0664 (UTC) FILETIME=[40DC5F80:01C98AF3] X-MIMETrack: Itemize by SMTP Server on MTAHM1100/Server/Accenture(Release 5.0.9a |January 7, 2002) at 02/09/2009 02:16:57 PM, Serialize by Router on MTAHM1100/Server/Accenture(Release 5.0.9a |January 7, 2002) at 02/09/2009 02:16:58 PM, Serialize complete at 02/09/2009 02:16:58 PM Content-Type: text/html; charset=utf-8 Hello from mars! --==IFJRGLKFGIR48542UHRUHIHD-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234211357.txt.eml000066400000000000000000000061551502127241700241510ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:29:17 2009 Return-Path: <> X-Original-To: bounces-main+agris.ameriksNEEXISTEE=gmail.com@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id 980243A08C for ; Mon, 9 Feb 2009 22:29:17 +0200 (EET) Received: by Albanis-3.local (Postfix) id 659FB328E84; Mon, 9 Feb 2009 22:29:16 +0200 (EET) Date: Mon, 9 Feb 2009 22:29:16 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+agris.ameriksNEEXISTEE=gmail.com@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="51830328E80.1234211356/Albanis-3.local" Message-Id: <20090209202916.659FB328E84@Albanis-3.local> This is a MIME-encapsulated message. --51830328E80.1234211356/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host gmail-smtp-in.l.google.com[209.85.218.64] said: 550-5.1.1 The email account that you tried to reach does not exist. Please 550-5.1.1 try double-checking the recipient's email address for typos 550-5.1.1 or unnecessary spaces. Learn more at 550 5.1.1 http://mail.google.com/support/bin/answer.py?answer=6596 20si2615223bwz.60 (in reply to RCPT TO command) --51830328E80.1234211356/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 51830328E80 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 22:29:11 +0200 (EET) Final-Recipient: rfc822; agris.ameriksNEEXISTEE@gmail.com Action: failed Status: 5.1.1 Remote-MTA: dns; gmail-smtp-in.l.google.com Diagnostic-Code: smtp; 550-5.1.1 The email account that you tried to reach does not exist. Please 550-5.1.1 try double-checking the recipient's email address for typos 550-5.1.1 or unnecessary spaces. Learn more at 550 5.1.1 http://mail.google.com/support/bin/answer.py?answer=6596 20si2615223bwz.60 --51830328E80.1234211356/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 51830328E80; Mon, 9 Feb 2009 22:29:11 +0200 (EET) Date: Mon, 9 Feb 2009 22:29:05 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: agris.ameriksNEEXISTEE@gmail.com Subject: subjecting Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209202911.51830328E80@Albanis-3.local> Hello from mars! --51830328E80.1234211356/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234211929.txt.eml000066400000000000000000000071331502127241700241530ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:38:49 2009 Return-Path: <> X-Original-To: bounces-main+jekaterina=tv5.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from blackbird.grafton.lv (blackbird.grafton.lv [159.148.13.8]) by ameriks (Postfix) with ESMTP id C45293A08C for ; Mon, 9 Feb 2009 22:38:49 +0200 (EET) Received: by blackbird.grafton.lv (Postfix) id 89ACB620076; Mon, 9 Feb 2009 22:38:49 +0200 (EET) Date: Mon, 9 Feb 2009 22:38:49 +0200 (EET) From: MAILER-DAEMON@blackbird.grafton.lv (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+jekaterina=tv5.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="02136620074.1234211929/blackbird.grafton.lv" Message-Id: <20090209203849.89ACB620076@blackbird.grafton.lv> This is a MIME-encapsulated message. --02136620074.1234211929/blackbird.grafton.lv Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host blackbird.grafton.lv. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : maildir delivery failed: Sorry, the user's maildir has overdrawn his diskspace quota, please try again later. --02136620074.1234211929/blackbird.grafton.lv Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; blackbird.grafton.lv X-Postfix-Queue-ID: 02136620074 X-Postfix-Sender: rfc822; bounces-main+jekaterina=tv5.lv@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 22:38:48 +0200 (EET) Final-Recipient: rfc822; jekaterina@tv5.lv Original-Recipient: rfc822;jekaterina@tv5.lv Action: failed Status: 5.2.3 Diagnostic-Code: X-Postfix; maildir delivery failed: Sorry, the user's maildir has overdrawn his diskspace quota, please try again later. --02136620074.1234211929/blackbird.grafton.lv Content-Description: Undelivered Message Content-Type: message/rfc822 Received: from Albanis-3.local (unknown [87.99.72.11]) by blackbird.grafton.lv (Postfix) with ESMTP id C92D475C04F for ; Mon, 9 Feb 2009 22:38:48 +0200 (EET) Received: by Albanis-3.local (Postfix, from userid 501) id 900D9328EDE; Mon, 9 Feb 2009 22:38:47 +0200 (EET) Date: Mon, 9 Feb 2009 22:33:44 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: jekaterina@tv5.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209203848.900D9328EDE@Albanis-3.local> X-GRAFTON-MailScanner-Information: Scanned by GRAFTON mailscanner service X-GRAFTON-MailScanner-ID: C92D475C04F.83B56 X-GRAFTON-MailScanner: Found to be clean X-GRAFTON-MailScanner-SpamCheck: not spam, SpamAssassin (not cached, score=1.656, required 5, BAYES_50 0.00, HTML_MESSAGE 0.00, HTML_MIME_NO_HTML_TAG 0.10, MIME_HTML_ONLY 1.46, RDNS_NONE 0.10) X-GRAFTON-MailScanner-SpamScore: s X-GRAFTON-MailScanner-From: bounces-main+jekaterina=tv5.lv@amerimail.lv X-Spam-Status: No Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --02136620074.1234211929/blackbird.grafton.lv-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234211931.txt.eml000066400000000000000000000070471502127241700241500ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:38:51 2009 Return-Path: <> X-Original-To: bounces-main+info.rietumuradio.lv=mail.studio7.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from mail.studio7pro.lv (mail.studio7pro.lv [159.148.225.2]) by ameriks (Postfix) with ESMTP id 177B73A08C for ; Mon, 9 Feb 2009 22:38:51 +0200 (EET) Received: by mail.studio7pro.lv (Postfix) id C3C2E108808A; Mon, 9 Feb 2009 22:38:50 +0200 (EET) Date: Mon, 9 Feb 2009 22:38:50 +0200 (EET) From: MAILER-DAEMON@mail.studio7pro.lv (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+info.rietumuradio.lv=mail.studio7.lv@amerimail.lv MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="B3C4D108806F.1234211930/mail.studio7pro.lv" Content-Transfer-Encoding: 7bit Message-Id: <20090209203850.C3C2E108808A@mail.studio7pro.lv> This is a MIME-encapsulated message. --B3C4D108806F.1234211930/mail.studio7pro.lv Content-Description: Notification Content-Type: text/plain This is the Postfix program at host mail.studio7pro.lv. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to If you do so, please include this problem report. You can delete your own text from the attached returned message. The Postfix program : mail for mail.studio7.lv loops back to myself --B3C4D108806F.1234211930/mail.studio7pro.lv Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; mail.studio7pro.lv X-Postfix-Queue-ID: B3C4D108806F X-Postfix-Sender: rfc822; bounces-main+info.rietumuradio.lv=mail.studio7.lv@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 22:38:50 +0200 (EET) Final-Recipient: rfc822; info.rietumuradio.lv@mail.studio7.lv Action: failed Status: 5.0.0 Diagnostic-Code: X-Postfix; mail for mail.studio7.lv loops back to myself --B3C4D108806F.1234211930/mail.studio7pro.lv Content-Description: Undelivered Message Content-Type: message/rfc822 Content-Transfer-Encoding: 7bit Received: from localhost (SpamFilter [10.10.0.216]) by mail.studio7pro.lv (Postfix) with ESMTP id B3C4D108806F for ; Mon, 9 Feb 2009 22:38:50 +0200 (EET) X-Virus-Scanned: amavisd-new at studio7pro.lv Received: from mail.studio7pro.lv ([10.10.0.2]) by localhost (mx1.studio7pro.lv [10.10.0.216]) (amavisd-new, port 10024) with ESMTP id uIwSm-zP27+k for ; Mon, 9 Feb 2009 22:38:48 +0200 (EET) Received: from Albanis-3.local (unknown [87.99.72.11]) by mail.studio7pro.lv (Postfix) with ESMTP for ; Mon, 9 Feb 2009 22:38:48 +0200 (EET) Received: by Albanis-3.local (Postfix, from userid 501) id A0627328EE6; Mon, 9 Feb 2009 22:38:47 +0200 (EET) Date: Mon, 9 Feb 2009 22:38:23 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: info.rietumuradio.lv@mail.studio7.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209203848.A0627328EE6@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --B3C4D108806F.1234211930/mail.studio7pro.lv-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234211932.txt.eml000066400000000000000000000055501502127241700241460ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:38:52 2009 Return-Path: <> X-Original-To: bounces-main+dace.balode=rigasvilni.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id 3A6443A08C for ; Mon, 9 Feb 2009 22:38:52 +0200 (EET) Received: by Albanis-3.local (Postfix) id 034F0328EF3; Mon, 9 Feb 2009 22:38:52 +0200 (EET) Date: Mon, 9 Feb 2009 22:38:52 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+dace.balode=rigasvilni.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="9764D328EE1.1234211932/Albanis-3.local" Message-Id: <20090209203852.034F0328EF3@Albanis-3.local> This is a MIME-encapsulated message. --9764D328EE1.1234211932/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host mail.rigasvilni.lv[159.148.4.100] said: 550 sorry, no mailbox here by that name. (#5.7.17) (in reply to RCPT TO command) --9764D328EE1.1234211932/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 9764D328EE1 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 22:38:47 +0200 (EET) Final-Recipient: rfc822; dace.balode@rigasvilni.lv Action: failed Status: 5.0.0 Remote-MTA: dns; mail.rigasvilni.lv Diagnostic-Code: smtp; 550 sorry, no mailbox here by that name. (#5.7.17) --9764D328EE1.1234211932/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 9764D328EE1; Mon, 9 Feb 2009 22:38:47 +0200 (EET) Date: Mon, 9 Feb 2009 22:36:06 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: dace.balode@rigasvilni.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209203848.9764D328EE1@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --9764D328EE1.1234211932/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234241665.txt.eml000066400000000000000000000055271502127241700241600ustar00rootroot00000000000000From MAILER-DAEMON Tue Feb 10 06:54:25 2009 Return-Path: <> X-Original-To: bounces-main+annas=sfl.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id 4B5B13A08C for ; Tue, 10 Feb 2009 06:54:25 +0200 (EET) Received: by Albanis-3.local (Postfix) id 022EF32925F; Tue, 10 Feb 2009 06:54:25 +0200 (EET) Date: Tue, 10 Feb 2009 06:54:25 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+annas=sfl.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="9406E328EE0.1234241665/Albanis-3.local" Message-Id: <20090210045425.022EF32925F@Albanis-3.local> This is a MIME-encapsulated message. --9406E328EE0.1234241665/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host mx.latnet.lv[92.240.64.15] said: 550 : Recipient address rejected: User unknown in virtual mailbox table (in reply to RCPT TO command) --9406E328EE0.1234241665/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 9406E328EE0 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Mon, 9 Feb 2009 22:38:47 +0200 (EET) Final-Recipient: rfc822; annas@sfl.lv Action: failed Status: 5.0.0 Remote-MTA: dns; mx.latnet.lv Diagnostic-Code: smtp; 550 : Recipient address rejected: User unknown in virtual mailbox table --9406E328EE0.1234241665/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 9406E328EE0; Mon, 9 Feb 2009 22:38:47 +0200 (EET) Date: Mon, 9 Feb 2009 22:35:24 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: annas@sfl.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090209203848.9406E328EE0@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --9406E328EE0.1234241665/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234285532.txt.eml000066400000000000000000000060771502127241700241620ustar00rootroot00000000000000From MAILER-DAEMON Tue Feb 10 19:05:32 2009 Return-Path: <> X-Original-To: bounces-main+doesntexistthisemaill=yahoo.com@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id 5AFF43A08C for ; Tue, 10 Feb 2009 19:05:32 +0200 (EET) Received: by Albanis-3.local (Postfix) id E6CFF329D08; Tue, 10 Feb 2009 19:05:31 +0200 (EET) Date: Tue, 10 Feb 2009 19:05:31 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+doesntexistthisemaill=yahoo.com@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="50514329496.1234285531/Albanis-3.local" Message-Id: <20090210170531.E6CFF329D08@Albanis-3.local> This is a MIME-encapsulated message. --50514329496.1234285531/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host d.mx.mail.yahoo.com[66.196.82.7] said: 554 delivery error: dd This user doesn't have a yahoo.com account (doesntexistthisemaill@yahoo.com) [0] - mta153.mail.re1.yahoo.com (in reply to end of DATA command) --50514329496.1234285531/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 50514329496 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Tue, 10 Feb 2009 18:56:27 +0200 (EET) Final-Recipient: rfc822; doesntexistthisemaill@yahoo.com Action: failed Status: 5.0.0 Remote-MTA: dns; d.mx.mail.yahoo.com Diagnostic-Code: smtp; 554 delivery error: dd This user doesn't have a yahoo.com account (doesntexistthisemaill@yahoo.com) [0] - mta153.mail.re1.yahoo.com --50514329496.1234285531/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 50514329496; Tue, 10 Feb 2009 18:56:27 +0200 (EET) Date: Tue, 10 Feb 2009 18:53:50 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: doesntexistthisemaill@yahoo.com Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090210165627.50514329496@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --50514329496.1234285531/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_1234285668.txt.eml000066400000000000000000000054511502127241700241670ustar00rootroot00000000000000From MAILER-DAEMON Tue Feb 10 19:07:48 2009 Return-Path: <> X-Original-To: bounces-main+agrisa=one.lv@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id EE9CC3A1A4 for ; Tue, 10 Feb 2009 19:07:47 +0200 (EET) Received: by Albanis-3.local (Postfix) id 89FF3329E3C; Tue, 10 Feb 2009 19:07:47 +0200 (EET) Date: Tue, 10 Feb 2009 19:07:47 +0200 (EET) From: MAILER-DAEMON@Albanis-3.local (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: bounces-main+agrisa=one.lv@amerimail.lv Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="7895C329E32.1234285667/Albanis-3.local" Message-Id: <20090210170747.89FF3329E3C@Albanis-3.local> This is a MIME-encapsulated message. --7895C329E32.1234285667/Albanis-3.local Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host Albanis-3.local. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system : host mail5.one.lv[62.85.54.62] said: 552 Account(s) does not have enough space (in reply to end of DATA command) --7895C329E32.1234285667/Albanis-3.local Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; Albanis-3.local X-Postfix-Queue-ID: 7895C329E32 X-Postfix-Sender: rfc822; bounces-main@amerimail.lv Arrival-Date: Tue, 10 Feb 2009 19:07:47 +0200 (EET) Final-Recipient: rfc822; agrisa@one.lv Action: failed Status: 5.0.0 Remote-MTA: dns; mail5.one.lv Diagnostic-Code: smtp; 552 Account(s) does not have enough space --7895C329E32.1234285667/Albanis-3.local Content-Description: Undelivered Message Content-Type: message/rfc822 Received: by Albanis-3.local (Postfix, from userid 501) id 7895C329E32; Tue, 10 Feb 2009 19:07:47 +0200 (EET) Date: Tue, 10 Feb 2009 19:07:09 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: agrisa@one.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090210170747.7895C329E32@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. --7895C329E32.1234285667/Albanis-3.local-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_01.txt.eml000066400000000000000000000044461502127241700246510ustar00rootroot00000000000000Return-path: Received: from abcycf1m.cf.ac.uk ([131.251.4.14]) by crane2.cf.ac.uk with esmtp (Exim 3.03 #1) id 14KSwL-0002Fo-00 for thornborrow@connect4free.net; Sun, 21 Jan 2001 22:26:25 +0000 Received: from ABCYCF1M/SpoolDir by ABCYCF1M.CF.AC.UK (Mercury 1.48); 21 Jan 01 22:27:45 GMT0BST Received: from SpoolDir by ABCYCF1M (Mercury 1.48); 21 Jan 01 22:27:36 GMT0BST Received: from ABCYCF1M/SpoolDir by ABCYCF1M.CF.AC.UK (Mercury 1.48) for ; 21 Jan 01 22:27:35 GMT0BST Resent-from: SENJT3.STF.ENCAP.HUMS.CF@ABCYCF1M.CF.AC.UK Resent-to: thornborrow@connect4free.net Resent-Date: Sun, 21 Jan 2001 22:27:35 GMT0BST X-Autoforward: 1 Received: from pigeon.cf.ac.uk (131.251.0.2) by ABCYCF1M.CF.AC.UK (Mercury 1.48) with ESMTP; 21 Jan 01 22:27:25 GMT0BST Received: from sprog.auc.dk ([130.225.58.12] ident=majordom) by pigeon.cf.ac.uk with esmtp (Exim 3.03 #1) id 14KSw1-00079N-00 for ThornborrowJ1@Cardiff.ac.uk; Sun, 21 Jan 2001 22:26:05 +0000 Received: (from majordom@localhost) by sprog.auc.dk (8.9.1a/8.9.1) id VAA05111 for languse-outgoing; Sun, 21 Jan 2001 21:53:40 +0100 (MET) X-Authentication-Warning: sprog.auc.dk: majordom set sender to owner-languse@sprog.auc.dk using -f Received: from crane2.cf.ac.uk (crane2.cf.ac.uk [131.251.0.25]) by sprog.auc.dk (8.9.1a/8.9.1) with ESMTP id VAA05107 for ; Sun, 21 Jan 2001 21:53:39 +0100 (MET) Received: from exim by crane2.cf.ac.uk with local (Exim 3.03 #1) id 14KRU7-000239-00 for languse@sprog.auc.dk; Sun, 21 Jan 2001 20:53:11 +0000 X-Failed-Recipients: thornborrow@connect4free.net From: Mail Delivery System To: languse@sprog.auc.dk Subject: [languse] Mail delivery failed: returning message to sender Message-Id: Date: Sun, 21 Jan 2001 20:53:11 +0000 Sender: owner-languse@sprog.auc.dk Precedence: bulk Reply-To: languse@sprog.auc.dk, Mail Delivery System Resent-Message-Id: This message was created automatically by mail delivery software. A message that you sent could not be delivered to all of its recipients. The following address(es) failed: thornborrow@connect4free.net: unrouteable mail domain "connect4free.net" schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_02.txt.eml000066400000000000000000000044461502127241700246520ustar00rootroot00000000000000Return-path: Received: from abcycf1m.cf.ac.uk ([131.251.4.14]) by crane2.cf.ac.uk with esmtp (Exim 3.03 #1) id 14KRU7-000237-00 for thornborrow@connect4free.net; Sun, 21 Jan 2001 20:53:11 +0000 Received: from ABCYCF1M/SpoolDir by ABCYCF1M.CF.AC.UK (Mercury 1.48); 21 Jan 01 20:54:31 GMT0BST Received: from SpoolDir by ABCYCF1M (Mercury 1.48); 21 Jan 01 20:54:06 GMT0BST Received: from ABCYCF1M/SpoolDir by ABCYCF1M.CF.AC.UK (Mercury 1.48) for ; 21 Jan 01 20:54:04 GMT0BST Resent-from: SENJT3.STF.ENCAP.HUMS.CF@ABCYCF1M.CF.AC.UK Resent-to: thornborrow@connect4free.net Resent-Date: Sun, 21 Jan 2001 20:54:04 GMT0BST X-Autoforward: 1 Received: from pigeon.cf.ac.uk (131.251.0.2) by ABCYCF1M.CF.AC.UK (Mercury 1.48) with ESMTP; 21 Jan 01 20:53:58 GMT0BST Received: from sprog.auc.dk ([130.225.58.12] ident=majordom) by pigeon.cf.ac.uk with esmtp (Exim 3.03 #1) id 14KRTZ-0006gv-00 for ThornborrowJ1@Cardiff.ac.uk; Sun, 21 Jan 2001 20:52:38 +0000 Received: (from majordom@localhost) by sprog.auc.dk (8.9.1a/8.9.1) id UAA04001 for languse-outgoing; Sun, 21 Jan 2001 20:20:58 +0100 (MET) X-Authentication-Warning: sprog.auc.dk: majordom set sender to owner-languse@sprog.auc.dk using -f Received: from crane2.cf.ac.uk (crane2.cf.ac.uk [131.251.0.25]) by sprog.auc.dk (8.9.1a/8.9.1) with ESMTP id UAA03997 for ; Sun, 21 Jan 2001 20:20:56 +0100 (MET) Received: from exim by crane2.cf.ac.uk with local (Exim 3.03 #1) id 14KQ2O-0001rj-00 for languse@sprog.auc.dk; Sun, 21 Jan 2001 19:20:28 +0000 X-Failed-Recipients: thornborrow@connect4free.net From: Mail Delivery System To: languse@sprog.auc.dk Subject: [languse] Mail delivery failed: returning message to sender Message-Id: Date: Sun, 21 Jan 2001 19:20:28 +0000 Sender: owner-languse@sprog.auc.dk Precedence: bulk Reply-To: languse@sprog.auc.dk, Mail Delivery System Resent-Message-Id: This message was created automatically by mail delivery software. A message that you sent could not be delivered to all of its recipients. The following address(es) failed: thornborrow@connect4free.net: unrouteable mail domain "connect4free.net" schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_03.txt.eml000066400000000000000000000064741502127241700246560ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.36.252.76 with SMTP id z76cs42358nzh; Mon, 28 Nov 2005 11:05:40 -0800 (PST) Received: by 10.36.251.8 with SMTP id y8mr3758555nzh; Mon, 28 Nov 2005 11:05:40 -0800 (PST) X-Forwarded-To: Agris.Ameriks@gmail.com X-Forwarded-For: ameriks@gmail.com Agris.Ameriks@gmail.com X-Gmail-Received: 499bf0339a8d4dd35bcd03d4596db02d01fdefc5 Delivered-To: ameriks@gmail.com Received: by 10.36.251.57 with SMTP id y57cs33742nzh; Mon, 28 Nov 2005 11:05:39 -0800 (PST) Received: by 10.48.3.5 with SMTP id 5mr493496nfc; Mon, 28 Nov 2005 11:05:39 -0800 (PST) Return-Path: <> Received: from apollo.lv (jupiter.apollo.lv [80.232.168.212]) by mx.gmail.com with ESMTP id o45si619017nfa.2005.11.28.11.05.09; Mon, 28 Nov 2005 11:05:39 -0800 (PST) Received-SPF: pass (gmail.com: best guess record for domain of apollo.lv designates 80.232.168.212 as permitted sender) Subject: Undeliverable mail: From: MAILER-DAEMON@apollo.lv To: Date: Mon, 28 Nov 2005 20:26:38 +0200 Message-ID: MIME-Version: 1.0 Content-Type: multipart/report; report-type="delivery-status"; boundary="_===110354846====apollo.lv===_" --_===110354846====apollo.lv===_ Content-Type: text/plain; charset="utf-8" Failed to deliver to '' Messages without To: fields are not accepted here --_===110354846====apollo.lv===_ Content-Type: message/delivery-status Reporting-MTA: dns; apollo.lv Original-Recipient: rfc822; Final-Recipient: system;<> Action: failed Status: 5.0.0 --_===110354846====apollo.lv===_ Content-Type: message/rfc822 Received: from zproxy.gmail.com ([64.233.162.194] verified) by apollo.lv (CommuniGate Pro SMTP 4.1.8) with ESMTP id 110354858 for agrisa@apollo.lv; Mon, 28 Nov 2005 20:26:37 +0200 Received: by zproxy.gmail.com with SMTP id s18so2823588nze for ; Mon, 28 Nov 2005 10:26:35 -0800 (PST) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:reply-to:subject:mime-version:content-type; b=tVddE1twi6/ew8gg2erlzR1AV4UgqT1v2d7+lazGsJr8a68QzxZLAhp/6NvW18kcpgqxRajkHztCLu0HYtQ+eR7iZLfM45EB6McOaZm0cfoySOIUl60KDFAH1LyW3Vl8lC7BNMCZwxomqaLI8kK2J8L/wOP/iqpdo0lFvDGFnsY= Received: by 10.37.15.37 with SMTP id s37mr3728494nzi; Mon, 28 Nov 2005 10:26:35 -0800 (PST) Received: by 10.36.251.57 with HTTP; Mon, 28 Nov 2005 10:26:35 -0800 (PST) Message-ID: <4abe0f090511281026t1436fa8dg844ce3c26184a927@mail.gmail.com> Date: Mon, 28 Nov 2005 20:26:35 +0200 From: Agris Ameriks Reply-To: Agris.Ameriks@gmail.com Subject: MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_12151_7196822.1133202395822" ------=_Part_12151_7196822.1133202395822 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline lalallalalaasd -- Ar cienu, Agris Ameriks RSD Kulturas komitejas vaditajs ------=_Part_12151_7196822.1133202395822 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline lalallalalaasd

--
Ar cienu,
Agris Ameriks
RS= D Kulturas komitejas vaditajs ------=_Part_12151_7196822.1133202395822-- --_===110354846====apollo.lv===_-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_04.txt.eml000066400000000000000000000066541502127241700246570ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.36.252.76 with SMTP id z76cs42363nzh; Mon, 28 Nov 2005 11:06:11 -0800 (PST) Received: by 10.36.25.8 with SMTP id 8mr3761324nzy; Mon, 28 Nov 2005 11:06:11 -0800 (PST) X-Forwarded-To: Agris.Ameriks@gmail.com X-Forwarded-For: ameriks@gmail.com Agris.Ameriks@gmail.com X-Gmail-Received: 2a7289d18d2f59a47dde79a70a1f4f6a690bcf51 Delivered-To: ameriks@gmail.com Received: by 10.36.251.57 with SMTP id y57cs33747nzh; Mon, 28 Nov 2005 11:06:11 -0800 (PST) Received: by 10.48.142.6 with SMTP id p6mr493395nfd; Mon, 28 Nov 2005 11:06:10 -0800 (PST) Return-Path: <> Received: from apollo.lv (jupiter.apollo.lv [80.232.168.212]) by mx.gmail.com with ESMTP id o45si619017nfa.2005.11.28.11.05.40; Mon, 28 Nov 2005 11:06:10 -0800 (PST) Received-SPF: pass (gmail.com: best guess record for domain of apollo.lv designates 80.232.168.212 as permitted sender) Subject: Undeliverable mail: asd From: MAILER-DAEMON@apollo.lv To: Date: Mon, 28 Nov 2005 20:27:07 +0200 Message-ID: MIME-Version: 1.0 Content-Type: multipart/report; report-type="delivery-status"; boundary="_===110354911====apollo.lv===_" --_===110354911====apollo.lv===_ Content-Type: text/plain; charset="utf-8" Failed to deliver to '' Messages without To: fields are not accepted here --_===110354911====apollo.lv===_ Content-Type: message/delivery-status Reporting-MTA: dns; apollo.lv Original-Recipient: rfc822; Final-Recipient: system;<> Action: failed Status: 5.0.0 --_===110354911====apollo.lv===_ Content-Type: message/rfc822 Received: from zproxy.gmail.com ([64.233.162.194] verified) by apollo.lv (CommuniGate Pro SMTP 4.1.8) with ESMTP id 110354880 for agrisa@apollo.lv; Mon, 28 Nov 2005 20:27:07 +0200 Received: by zproxy.gmail.com with SMTP id s18so2823588nze for ; Mon, 28 Nov 2005 10:27:06 -0800 (PST) DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=received:message-id:date:from:reply-to:subject:cc:mime-version:content-type; b=lj7BO/1r1H6jaGsjuCV7+4IlSZdoBt1PCSkVXBc0Uh97j+EWpsbfAYPSbbqo6AzxkkCSaVLMXG5cszvroWXvUsKD1tVB0IY7DRP2mP6VEb0C60tHRRwPJkAGbaqq1Q9T8ih1OQGKdNdBxxod7gH5XhK2uXZZiAkaCbLBNPmU5es= Received: by 10.36.250.46 with SMTP id x46mr3723067nzh; Mon, 28 Nov 2005 10:27:06 -0800 (PST) Received: by 10.36.251.57 with HTTP; Mon, 28 Nov 2005 10:27:06 -0800 (PST) Message-ID: <4abe0f090511281027i654be30bt5c2405dc847bcbc4@mail.gmail.com> Date: Mon, 28 Nov 2005 20:27:06 +0200 From: Agris Ameriks Reply-To: Agris.Ameriks@gmail.com Subject: asd Cc: Agris Ameriks 2 , Agris Ameriks , ameriks@gmail.com MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_12159_32010714.1133202426241" ------=_Part_12159_32010714.1133202426241 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline alalallalala -- Ar cienu, Agris Ameriks RSD Kulturas komitejas vaditajs ------=_Part_12159_32010714.1133202426241 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline alalallalala

--
Ar cienu,
Agris Ameriks
RSD = Kulturas komitejas vaditajs ------=_Part_12159_32010714.1133202426241-- --_===110354911====apollo.lv===_-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_05.txt.eml000066400000000000000000000070161502127241700246510ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.100.94.18 with SMTP id r18cs649481anb; Tue, 8 May 2007 09:35:28 -0700 (PDT) Received: by 10.78.145.5 with SMTP id s5mr377920hud.1178642127951; Tue, 08 May 2007 09:35:27 -0700 (PDT) Return-Path: <> Received: from smtp2.apollo.lv (smtp2.apollo.lv [80.232.168.229]) by mx.google.com with ESMTP id 55si1194537ugq.2007.05.08.09.35.27; Tue, 08 May 2007 09:35:27 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of smtp2.apollo.lv designates 80.232.168.229 as permitted sender) X-Cloudmark-Score: 0.000000 [] X-Virusscan: Clamd Subject: Undeliverable mail: Labdien! From: To: Date: Tue, 08 May 2007 19:35:27 +0300 Message-ID: X-MAPI-Message-Class: REPORT.IPM.Note.NDR MIME-Version: 1.0 Content-Type: multipart/report; report-type="delivery-status"; boundary="_===92533048====smtp2.apollo.lv===_" --_===92533048====smtp2.apollo.lv===_ Content-Type: text/plain; charset="utf-8" Failed to deliver to 'evor@apollo.lv' SMTP module(domain pop.apollo.lv) reports: host pop.apollo.lv says: 550 evor@apollo.lv unknown user account --_===92533048====smtp2.apollo.lv===_ Content-Type: message/delivery-status Reporting-MTA: dns; smtp2.apollo.lv Original-Recipient: rfc822; Final-Recipient: rfc822; Action: failed Status: 5.0.0 --_===92533048====smtp2.apollo.lv===_ Content-Type: message/rfc822 Received: from wr-out-0506.google.com ([64.233.184.231] verified) by smtp2.apollo.lv (CommuniGate Pro SMTP 5.0.10) with ESMTP id 92533044 for evor@apollo.lv; Tue, 08 May 2007 19:35:26 +0300 Received: by wr-out-0506.google.com with SMTP id 25so1879201wry for ; Tue, 08 May 2007 09:35:20 -0700 (PDT) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type:content-transfer-encoding:content-disposition; b=Sa8LZ3AQtQcbyYJx8yrTBpc8FMFhKCE7Hm1Vm4QSvn+teR7CpqPcEGUSWSNklzqPAzN+VsnFUgBwnxcRNi0+YddsnmgZWmI9V6HaUniXHxvAlId36z4UjTKSrGXb3dv/xwcTRut8AFmKdACrnxpiRPp+R1uqHbR2pMFotkynuCs= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type:content-transfer-encoding:content-disposition; b=giHBorElbW7SdOhu+wcknrVopvrUVV9io1n7dG3HI0+bZfYRYh1MAb2fAMbWGCvG13UjU1TZHds84xZ0xvKDGcKVIhCXb7mfo3XZCQoPyup7IHrcr/TPlLEf1MpKw1oCmk5NS1u5nVVYf2FZsI4kGZvoGeJHP68orx5PmlIEQXQ= Received: by 10.100.230.13 with SMTP id c13mr3322842anh.1178642116550; Tue, 08 May 2007 09:35:16 -0700 (PDT) Received: by 10.100.94.18 with HTTP; Tue, 8 May 2007 09:35:16 -0700 (PDT) Message-ID: Date: Tue, 8 May 2007 19:35:16 +0300 From: "Agris Ameriks" To: evor@apollo.lv Subject: Labdien! MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-13; format=flowed Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCkVzIGludGVyZXPnam9zLCBjaWsgdmFy53R1IGl6bWFrc+J0IDE1LnNlcHRlbWJy 7iBub2Ryb/BpbuJ0IGvicnTuYnUKVudybWFuZXMgZOJyeuIgbm8gMTQ6MDAgLSAyMzowMC4gKHBh c+JrdW1zIG5vcml053Mgbm8gMTU6MDAgLSAyMjoyMCkuCgpNYW4gaW50ZXJlc+csIGNpayBpem1h a3PidHU6CjEpIDYgYXBzYXJnaS4KMikgMTAgYXBzYXJnaS4KCi0tIApBciBjaWXydSwKQWdyaXMg QW1lcmlrcwpNb2IudGVsLiAyIDY0NjExMDEKRS1wYXN0czogQWdyaXMuQW1lcmlrc0BnbWFpbC5j b20K --_===92533048====smtp2.apollo.lv===_-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_06.txt.eml000066400000000000000000000040701502127241700246470ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.100.94.18 with SMTP id r18cs650238anb; Tue, 8 May 2007 09:44:00 -0700 (PDT) Received: by 10.100.240.19 with SMTP id n19mr5850181anh.1178642640024; Tue, 08 May 2007 09:44:00 -0700 (PDT) Return-Path: <> Received: by 10.100.240.19 with SMTP id n19mr8655080anh; Tue, 08 May 2007 09:44:00 -0700 (PDT) Message-ID: <16368e24c8042ff81fe0345a2d1c5@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Tue, 08 May 2007 09:44:00 -0700 (PDT) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: info@koblenz.lv Technical details of permanent failure: PERM_FAILURE: SMTP Error (state 9): 550 5.7.1 Your message (sent through 209.85.132.244) was blocked by ROTA DNSBL. If you are not a spammer, open http://www.rota.lv/DNSBL and follow instructions or call +371 7019029, or send an e-mail message from another address to dz@ROTA.lv with the blocked sender e-mail name. ----- Original message ----- Received: by 10.100.240.19 with SMTP id n19mr5850084anh.1178642634965; Tue, 08 May 2007 09:43:54 -0700 (PDT) Received: by 10.100.94.18 with HTTP; Tue, 8 May 2007 09:43:54 -0700 (PDT) Message-ID: Date: Tue, 8 May 2007 19:43:54 +0300 From: "Agris Ameriks" To: agrisa@apollo.lv Subject: Labdien! MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-13; format=flowed Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCkVzIGludGVyZXPnam9zLCBjaWsgdmFy53R1IGl6bWFrc+J0IDE1LnNlcHRlbWJy 7iBub2Ryb/BpbuJ0IGvicnTuYnUKVudybWFuZXMgZOJyeuIgbm8gMTQ6MDAgLSAyMzowMC4gKHBh c+JrdW1zIG5vcml053Mgbm8gMTU6MDAgLSAyMjoyMCkuCgpNYW4gaW50ZXJlc+csIGNpayBpem1h a3PidHU6CjEpIDYgYXBzYXJnaS4KMikgMTAgYXBzYXJnaS4KCi0tIApBciBjaWXydSwKQWdyaXMg QW1lcmlrcwpNb2IudGVsLiAyIDY0NjExMDEKRS1wYXN0czogQWdyaXMuQW1lcmlrc0BnbWFpbC5j b20K ----- End of message ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_07.txt.eml000066400000000000000000000103051502127241700246460ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.66.225.19 with SMTP id x19cs81231ugg; Mon, 4 Jun 2007 15:20:37 -0700 (PDT) Received: by 10.67.89.6 with SMTP id r6mr7631ugl.1180995637243; Mon, 04 Jun 2007 15:20:37 -0700 (PDT) Return-Path: <> Received: from avalon.telekom.lv ([194.8.16.6]) by mx.google.com with ESMTP id c22si1797563ika.2007.06.04.15.20.31; Mon, 04 Jun 2007 15:20:37 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of avalon.telekom.lv designates 194.8.16.6 as permitted sender) Subject: Undeliverable mail: =?ISO-8859-13?Q?Atbalsta_l=FBgums?= From: To: Date: Tue, 05 Jun 2007 01:20:30 +0300 Message-ID: X-MAPI-Message-Class: REPORT.IPM.Note.NDR MIME-Version: 1.0 Content-Type: multipart/report; report-type="delivery-status"; boundary="_===38387797====avalon.telekom.lv===_" --_===38387797====avalon.telekom.lv===_ Content-Type: text/plain; charset="utf-8" Failed to deliver to 'ilona.kalnina@citrus.lv' SMTP module(domain [10.2.9.112]) reports: host 10.2.9.112 says: 550 5.1.1 User unknown --_===38387797====avalon.telekom.lv===_ Content-Type: message/delivery-status Reporting-MTA: dns; avalon.telekom.lv Original-Recipient: rfc822; Final-Recipient: rfc822; Action: failed Status: 5.0.0 --_===38387797====avalon.telekom.lv===_ Content-Type: message/rfc822 Received: from ug-out-1314.google.com ([66.249.92.174] verified) by avalon.telekom.lv (CommuniGate Pro SMTP 5.0.8) with ESMTP id 38387776 for ilona.kalnina@citrus.lv; Tue, 05 Jun 2007 01:20:10 +0300 Received: by ug-out-1314.google.com with SMTP id y2so152uge for ; Mon, 04 Jun 2007 15:19:54 -0700 (PDT) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type; b=l10L7A+KF49Pwyz92+DFPA/wZLu/uOlsEYg0PlwTfaVpXI/OKFJXSfhw1seTSbKWTnnQK60/hVdRqvdzBuGCQaZo5fkdhRFVQ6U24aBna+4L3tRyROlpOwVVNcWvdZydaJPuRWf8XLVJjy0z/850mX9tlCk8vdFOcAMzoS+/YwU= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type; b=epPNHV4IgrC3VhiIdXBXrbxBObboO2zqkB9gca8ZMpQBaH6TDRIhpFzukgJdEZBQHQih2W1trVQ1qPDYJsH3vz0IN5jiGbREfmqobCtrKR36zMw4sJw11YLLSS0j7ej4dJhwg7Vt7YLSM4fXtqsNuODv1ykPcEEBuxTsWlD8XW8= Received: by 10.67.32.16 with SMTP id k16mr3381783ugj.1180995594284; Mon, 04 Jun 2007 15:19:54 -0700 (PDT) Received: by 10.66.225.19 with HTTP; Mon, 4 Jun 2007 15:19:54 -0700 (PDT) Message-ID: Date: Tue, 5 Jun 2007 01:19:54 +0300 From: "Agris Ameriks" To: ilona.kalnina@citrus.lv Subject: =?ISO-8859-13?Q?Atbalsta_l=FBgums?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_13894_24991589.1180995594034" ------=_Part_13894_24991589.1180995594034 Content-Type: text/plain; charset=ISO-8859-13; format=flowed Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCjE1LnNlcHRlbWJy7iBGaXptYXR1IGF0YmFsc3RhIGZvbmRzIHLua29zIGphdW5p ZfB1IGJ1cnpp8nUgIlRhdmEgcGFzYXVsZSEiLgoKUGFz4mt1bXMgbm90aWtzIFLuZ2FzIERvbWVz IEl6Z2zudO5iYXMsIGphdW5hdG5lcyB1biBzcG9ydGEKZGVwYXJ0YW1lbnRhIHLua290YWriIEph dW5pZfB1IG3nbmXwYSBpZXR2YXJvcy4gUu5nYXMgRG9tZSBwaWXw7WlyCjQnMDAwIExzIGzuZHpm aW5hbnPnanVtdS4KClbnbGFtaWVzIGz7Z3QgSvtzdSBwYWzuZHruYnUg8O4gcHJvamVrdGEg7nN0 ZW5v8GFu4i4KClPua+JrcyBwcm9qZWt0YSBhcHJha3N0cyBwaWV2aWVub3RzIHBpZWxpa3Vt4i4K CgotLSAKQXIgY2ll8nUsCkFncmlzIEFtZXJpa3MKRml6bWF0dSBhdGJhbHN0YSBmb25kcyBwcmll a/Bz52TndOJqcwpNb2IudGVsLiAyIDY0NjExMDEKRS1wYXN0czogQWdyaXMuQW1lcmlrc0BnbWFp bC5jb20K ------=_Part_13894_24991589.1180995594034 Content-Type: application/pdf; name="Citrus_Solutions.pdf" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="Citrus_Solutions.pdf" X-Attachment-Id: f_f2ji9xzu MzE3MiAwMDAwMCBuDQowMDAwNzI2OTE4IDAwMDAwIG4NCnRyYWlsZXINCjw8L1NpemUgMzQ2Pj4N CnN0YXJ0eHJlZg0KMTE2DQolJUVPRg0KAttachmentTruncated ------=_Part_13894_24991589.1180995594034-- --_===38387797====avalon.telekom.lv===_-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_08.txt.eml000066400000000000000000000036711502127241700246570ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs83816ugm; Mon, 17 Sep 2007 13:18:52 -0700 (PDT) Received: by 10.66.242.19 with SMTP id p19mr7552403ugh.1190060332137; Mon, 17 Sep 2007 13:18:52 -0700 (PDT) Return-Path: <> Received: by 10.66.242.19 with SMTP id p19mr10626800ugh; Mon, 17 Sep 2007 13:18:52 -0700 (PDT) Message-ID: <0014850b2bf1043a5a835bbd57c45e@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Mon, 17 Sep 2007 13:18:52 -0700 (PDT) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: jauatkales@gmial.com Technical details of permanent failure: TEMP_FAILURE: Could not initiate SMTP conversation with any hosts: [gmial.com (1): Connection timed out] ----- Original message ----- Received: by 10.66.242.19 with SMTP id p19mr3910432ugh.1189785128812; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Received: by 10.67.102.1 with HTTP; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Message-ID: Date: Fri, 14 Sep 2007 18:52:08 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?prof._Ivars_L=E2cis_uzst=E2sies_ar_lekciju?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_868_30357988.1189785128799" ------=_Part_868_30357988.1189785128799 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCk5vc/t0dSBwcmVzZXMgcmVs7nppIHBhciBwYXPia3VtdSAxNS5zZXB0ZW1icu4g RXNwbGFu4mTnLgoKLS0gCkFyIGNpZfJ1LApBZ3JpcyBBbWVyaWtzCk1vYi50ZWwuIDIgNjQ2MTEw MQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdtYWlsLmNvbQo= ------=_Part_868_30357988.1189785128799 Content-Type: application/msword; name="relize_rektori.doc" ----- Message truncated ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_09.txt.eml000066400000000000000000000036671502127241700246650ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs79686ugm; Mon, 17 Sep 2007 11:17:15 -0700 (PDT) Received: by 10.66.242.19 with SMTP id p19mr7441638ugh.1190053034458; Mon, 17 Sep 2007 11:17:14 -0700 (PDT) Return-Path: <> Received: by 10.66.242.19 with SMTP id p19mr10490766ugh; Mon, 17 Sep 2007 11:17:14 -0700 (PDT) Message-ID: <0014850b2bf1043a58d061fe5bc312@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Mon, 17 Sep 2007 11:17:14 -0700 (PDT) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: gintsgrube@pop.ml.lv Technical details of permanent failure: TEMP_FAILURE: Could not initiate SMTP conversation with any hosts: [pop.ml.lv (1): Connection dropped] ----- Original message ----- Received: by 10.66.242.19 with SMTP id p19mr3910432ugh.1189785128812; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Received: by 10.67.102.1 with HTTP; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Message-ID: Date: Fri, 14 Sep 2007 18:52:08 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?prof._Ivars_L=E2cis_uzst=E2sies_ar_lekciju?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_868_30357988.1189785128799" ------=_Part_868_30357988.1189785128799 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCk5vc/t0dSBwcmVzZXMgcmVs7nppIHBhciBwYXPia3VtdSAxNS5zZXB0ZW1icu4g RXNwbGFu4mTnLgoKLS0gCkFyIGNpZfJ1LApBZ3JpcyBBbWVyaWtzCk1vYi50ZWwuIDIgNjQ2MTEw MQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdtYWlsLmNvbQo= ------=_Part_868_30357988.1189785128799 Content-Type: application/msword; name="relize_rektori.doc" ----- Message truncated ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_10.txt.eml000066400000000000000000000162331502127241700246460ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs29105ugm; Sat, 15 Sep 2007 09:07:11 -0700 (PDT) Received: by 10.86.84.5 with SMTP id h5mr2204525fgb.1189872431018; Sat, 15 Sep 2007 09:07:11 -0700 (PDT) Return-Path: <> Received: from mail.studio7.lv ([159.148.225.2]) by mx.google.com with ESMTP id 13si4519655fks.2007.09.15.09.07.10; Sat, 15 Sep 2007 09:07:10 -0700 (PDT) Received-SPF: neutral (google.com: 159.148.225.2 is neither permitted nor denied by best guess record for domain of mail.studio7.lv) client-ip=159.148.225.2; Authentication-Results: mx.google.com; spf=neutral (google.com: 159.148.225.2 is neither permitted nor denied by best guess record for domain of mail.studio7.lv) smtp.mail= Received: by mail.studio7.lv (Postfix) id 7079F1088055; Sat, 15 Sep 2007 19:07:08 +0300 (EEST) Date: Sat, 15 Sep 2007 19:07:08 +0300 (EEST) From: MAILER-DAEMON@mail.studio7.lv (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: agris.ameriks@gmail.com MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="8BCE21088059.1189872428/mail.studio7.lv" Message-Id: <20070915160708.7079F1088055@mail.studio7.lv> This is a MIME-encapsulated message. --8BCE21088059.1189872428/mail.studio7.lv Content-Description: Notification Content-Type: text/plain This is the Postfix program at host mail.studio7.lv. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to If you do so, please include this problem report. You can delete your own text from the attached returned message. The Postfix program : temporary failure --8BCE21088059.1189872428/mail.studio7.lv Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; mail.studio7.lv X-Postfix-Queue-ID: 8BCE21088059 X-Postfix-Sender: rfc822; agris.ameriks@gmail.com Arrival-Date: Fri, 14 Sep 2007 18:52:25 +0300 (EEST) Final-Recipient: rfc822; info.rietumuradio.lv@mail.studio7.lv Action: failed Status: 4.0.0 Diagnostic-Code: X-Postfix; temporary failure --8BCE21088059.1189872428/mail.studio7.lv Content-Description: Undelivered Message Content-Type: message/rfc822 Received: from localhost (localhost.localdomain [127.0.0.1]) by mail.studio7.lv (Postfix) with ESMTP id 8BCE21088059 for ; Fri, 14 Sep 2007 18:52:25 +0300 (EEST) X-Virus-Scanned: amavisd-new at studio7.lv Received: from mail.studio7.lv ([127.0.0.1]) by localhost (smtp.studio7.lv [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ptzaz7um-NBV for ; Fri, 14 Sep 2007 18:52:23 +0300 (EEST) Received: from hu-out-0506.google.com (hu-out-0506.google.com [72.14.214.227]) by mail.studio7.lv (Postfix) with ESMTP id 9F8D2108806D for ; Fri, 14 Sep 2007 18:52:22 +0300 (EEST) Received: by hu-out-0506.google.com with SMTP id 38so472432huc for ; Fri, 14 Sep 2007 08:52:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type; bh=4vXa1w6DKyZv8zm1ehZCNcwq4zu82fUl9OQIU6HbeGE=; b=rswuEvzRFbiKcOsstWjGjv1coaMxHwvSVfu8tIn4I9K3C/JY6JK2zK6C2vOPc9DUgaCyFxnXsv7Vf4ESuAcMTaqP3RCabN//lJ9YGixCxvdTZhgaS9vs9Ze6WV68Mw3lCB1eg5QC7CmhHlegDpAtBWV/NLqAPa9WiSun+T5eFxE= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type; b=dsax+33YeZDwUJY8uMkbOIOiBLaklYI33wM+tVcxaiESRe8TbTgOvPhQyW6x45knhFvyR9PNE7lDpq3Xz00OMMT6UnM9wRA9q7U0Pd2Vr2+lCw4DcXyfCo/PhBdYydODibWr//M0WmU9eL/S9zLSMW87gZPe0v4PNFd4hQSev9w= Received: by 10.66.242.19 with SMTP id p19mr3910432ugh.1189785128812; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Received: by 10.67.102.1 with HTTP; Fri, 14 Sep 2007 08:52:08 -0700 (PDT) Message-ID: Date: Fri, 14 Sep 2007 18:52:08 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?prof._Ivars_L=E2cis_uzst=E2sies_ar_lekciju?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_868_30357988.1189785128799" ------=_Part_868_30357988.1189785128799 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCk5vc/t0dSBwcmVzZXMgcmVs7nppIHBhciBwYXPia3VtdSAxNS5zZXB0ZW1icu4g RXNwbGFu4mTnLgoKLS0gCkFyIGNpZfJ1LApBZ3JpcyBBbWVyaWtzCk1vYi50ZWwuIDIgNjQ2MTEw MQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdtYWlsLmNvbQo= ------=_Part_868_30357988.1189785128799 Content-Type: application/msword; name="relize_rektori.doc" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="relize_rektori.doc" X-Attachment-Id: f_f6kvbbns 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAALAAAAAAAAAAA EAAALgAAAAEAAAD+////AAAAACsAAAD///////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////s pcEAW4AJBAAA8BK/AAAAAAAAEAAAAAAACAAAEhQAAA4AYmpiaqz6rPoAAAAAAAAAAAAAAAAAAAAA AAAJBBYANBYAAM6QAQDOkAEApAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAA AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAALcAAAAAAJIFAAAAAAAAkgUAANUS AAAAAAAA1RIAAAAAAADVEgAAAAAAANUSAAAAAAAA1RIAABQAAAAAAAAAAAAAAP////8AAAAA6RIA AAAAAADpEgAAAAAAAOkSAAAAAAAA6RIAAAwAAAD1EgAADAAAAOkSAAAAAAAArxkAADABAAABEwAA AAAAAAETAAAAAAAAARMAAAAAAAABEwAAAAAAAAETAAAAAAAA3BMAAAAAAADcEwAAAAAAANwTAAAA AAAAIhkAAAIAAAAkGQAAAAAAACQZAAAAAAAAJBkAAAAAAAAkGQAAAAAAACQZAAAAAAAAJBkAACQA AADfGgAAogIAAIEdAAA2AAAASBkAACEAAAAAAAAAAAAAAAAAAAAAAAAA1RIAAAAAAADcEwAAAAAA AAAAAAAAAAAAAAAAAAAAAADcEwAAAAAAANwTAAAAAAAA3BMAAAAAAADcEwAAAAAAAEgZAAAAAAAA AAAAAAAAAADVEgAAAAAAANUSAAAAAAAAARMAAAAAAAAAAAAAAAAAAAETAADbAAAAaRkAABYAAADa GAAAAAAAANoYAAAAAAAA2hgAAAAAAADcEwAAcgQAANUSAAAAAAAAARMAAAAAAADVEgAAAAAAAAET AAAAAAAAIhkAAAAAAAAAAAAAAAAAANoYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA3BMAAAAAAAAiGQAAAAAAAAAAAAAAAAAA2hgAAAAAAAAAAAAA AAAAANoYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2hgAAAAAAAABEwAAAAAAAP////8AAAAAEA+LEuf2 xwEAAAAAAAAAAOkSAAAAAAAAThgAAGoAAADaGAAAAAAAAAAAAAAAAAAADhkAABQAAAB/GQAAMAAA AK8ZAAAAAAAA2hgAAAAAAAC3HQAAAAAAALgYAAAiAAAAtx0AAAAAAADaGAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAALcdAAAAAAAAAAAAAAAAAADVEgAAAAAAANoYAAA0AAAA3BMAAAAAAADcEwAAAAAAANoY AAAAAAAA3BMAAAAAAADcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3BMA --8BCE21088059.1189872428/mail.studio7.lv-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_11.txt.eml000066400000000000000000001104051502127241700246430ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs7276ugm; Fri, 14 Sep 2007 08:52:48 -0700 (PDT) Received: by 10.78.146.11 with SMTP id t11mr1168394hud.1189785168336; Fri, 14 Sep 2007 08:52:48 -0700 (PDT) Return-Path: <> Received: from mao.leta.lv (mao.leta.lv [62.85.110.238]) by mx.google.com with ESMTP id s36si384903hub.2007.09.14.08.52.47; Fri, 14 Sep 2007 08:52:48 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of mao.leta.lv designates 62.85.110.238 as permitted sender) client-ip=62.85.110.238; Authentication-Results: mx.google.com; spf=pass smtp.mail= Received: from letaexc.leta.lv (letaexc.leta.lv [192.168.0.2]) by mao.leta.lv (Postfix) with ESMTP id 6CE9B1C1F121 for ; Fri, 14 Sep 2007 18:52:46 +0300 (EEST) Received: by letaexc.leta.lv with Internet Mail Service (5.5.2657.72) id ; Fri, 14 Sep 2007 18:53:01 +0300 Message-ID: From: System Administrator To: agris.ameriks@gmail.com Subject: =?WINDOWS-1257?Q?Undeliverable=3A_prof=2E_Ivars_L=E2cis_uzst=E2?= =?WINDOWS-1257?Q?sies_ar_lekciju?= Date: Fri, 14 Sep 2007 18:53:01 +0300 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2657.72) X-MS-Embedded-Report: Content-Type: multipart/mixed; boundary="----_=_NextPart_000_01C7F6E7.540B3540" This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible. ------_=_NextPart_000_01C7F6E7.540B3540 Content-Type: text/plain; charset="WINDOWS-1257" Content-Transfer-Encoding: quoted-printable Your message To: Agris.Ameriks@gmail.com Subject: prof. Ivars L=E2cis uzst=E2sies ar lekciju Sent: Fri, 14 Sep 2007 18:52:08 +0300 did not reach the following recipient(s): M=E2rti=F2=F0 Luka=F0evi=E8s on Fri, 14 Sep 2007 18:52:54 +0300 The recipient was unavailable to take delivery of the message The MTS-ID of the original message is: c=3Dus;a=3D ;p=3Dleta;l=3DLETAEXC0709141552S7ZB4DDP MSEXCH:MSExchangeMTA:LETA:LETAEXC ------_=_NextPart_000_01C7F6E7.540B3540 Content-Type: message/rfc822 Message-ID: From: Agris Ameriks To: Agris.Ameriks@gmail.com Subject: =?WINDOWS-1257?Q?prof=2E_Ivars_L=E2cis_uzst=E2sies_ar_lekciju?= Date: Fri, 14 Sep 2007 18:52:08 +0300 MIME-Version: 1.0 X-Mailer: Internet Mail Service (5.5.2657.72) X-MS-Embedded-Report: Content-Type: multipart/mixed; boundary="----_=_NextPart_002_01C7F6E7.540B3540" ------_=_NextPart_002_01C7F6E7.540B3540 Content-Type: text/plain; charset="WINDOWS-1257" Content-Transfer-Encoding: quoted-printable Labdien! Nos=FBtu preses rel=EEzi par pas=E2kumu 15.septembr=EE Esplan=E2d=E7. --=20 Ar cie=F2u, Agris Ameriks Mob.tel. 2 6461101 E-pasts: Agris.Ameriks@gmail.com ------_=_NextPart_002_01C7F6E7.540B3540 Content-Type: application/msword; name="relize_rektori.doc" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="relize_rektori.doc" 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAALAAAAAAAAAAA EAAALgAAAAEAAAD+////AAAAACsAAAD///////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////s pcEAW4AJBAAA8BK/AAAAAAAAEAAAAAAACAAAEhQAAA4AYmpiaqz6rPoAAAAAAAAAAAAAAAAAAAAA AAAJBBYANBYAAM6QAQDOkAEApAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAA AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAALcAAAAAAJIFAAAAAAAAkgUAANUS AAAAAAAA1RIAAAAAAADVEgAAAAAAANUSAAAAAAAA1RIAABQAAAAAAAAAAAAAAP////8AAAAA6RIA AAAAAADpEgAAAAAAAOkSAAAAAAAA6RIAAAwAAAD1EgAADAAAAOkSAAAAAAAArxkAADABAAABEwAA AAAAAAETAAAAAAAAARMAAAAAAAABEwAAAAAAAAETAAAAAAAA3BMAAAAAAADcEwAAAAAAANwTAAAA AAAAIhkAAAIAAAAkGQAAAAAAACQZAAAAAAAAJBkAAAAAAAAkGQAAAAAAACQZAAAAAAAAJBkAACQA AADfGgAAogIAAIEdAAA2AAAASBkAACEAAAAAAAAAAAAAAAAAAAAAAAAA1RIAAAAAAADcEwAAAAAA AAAAAAAAAAAAAAAAAAAAAADcEwAAAAAAANwTAAAAAAAA3BMAAAAAAADcEwAAAAAAAEgZAAAAAAAA AAAAAAAAAADVEgAAAAAAANUSAAAAAAAAARMAAAAAAAAAAAAAAAAAAAETAADbAAAAaRkAABYAAADa GAAAAAAAANoYAAAAAAAA2hgAAAAAAADcEwAAcgQAANUSAAAAAAAAARMAAAAAAADVEgAAAAAAAAET AAAAAAAAIhkAAAAAAAAAAAAAAAAAANoYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA3BMAAAAAAAAiGQAAAAAAAAAAAAAAAAAA2hgAAAAAAAAAAAAA AAAAANoYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2hgAAAAAAAABEwAAAAAAAP////8AAAAAEA+LEuf2 xwEAAAAAAAAAAOkSAAAAAAAAThgAAGoAAADaGAAAAAAAAAAAAAAAAAAADhkAABQAAAB/GQAAMAAA AK8ZAAAAAAAA2hgAAAAAAAC3HQAAAAAAALgYAAAiAAAAtx0AAAAAAADaGAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAALcdAAAAAAAAAAAAAAAAAADVEgAAAAAAANoYAAA0AAAA3BMAAAAAAADcEwAAAAAAANoY AAAAAAAA3BMAAAAAAADcEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3BMA AAAAAADcEwAAAAAAANwTAAAAAAAASBkAAAAAAABIGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA2hgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANwTAAAA AAAA3BMAAAAAAADcEwAAAAAAAK8ZAAAAAAAA3BMAAAAAAADcEwAAAAAAANwTAAAAAAAA3BMAAAAA AAAAAAAAAAAAAP////8AAAAA/////wAAAAD/////AAAAAAAAAAAAAAAA/////wAAAAD/////AAAA AP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA /////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAALcdAAAAAAAA3BMAAAAAAADc EwAAAAAAANwTAAAAAAAA3BMAAAAAAADcEwAAAAAAANwTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADcEwAAAAAAANwTAAAAAAAA3BMA AAAAAACSBQAACQwAAJsRAAA6AQAABQASAQAACQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADIAMAAw ADcALgBnAGEAZABhACAAMQA0AC4AcwBlAHAAdABlAG0AYgByAGkAcwAuAA0ASQBuAGYAbwByAG0A AQFjAGkAagB1ACAAcwBhAGcAYQB0AGEAdgBvAGoAYQAgAEEAZwByAGkAcwAgAEEAbQBlAHIAaQBr AHMADQAxADUALgBzAGUAcAB0AGUAbQBiAHIAKwEgAGUAawBzAC0AcgBlAGsAdABvAHIAcwAgAHAA cgBvAGYALgAgAEkAdgBhAHIAcwAgAEwAAQFjAGkAcwAgAGwAYQBzACsBcwAgAGwAZQBrAGMAaQBq AHUALgANAFMAdAB1AGQAZQBuAHQAdQAgAFAAAQFyAHQAaQBqAGEAIABpAGUAdAB2AGEAcgBvAHMA IAAxADUALgBzAGUAcAB0AGUAbQBiAHIAKwEgAEUAcwBwAGwAYQBuAAEBZAATASAAcAByAG8AZgAu ACAASQAuAEwAAQFjAGkAcwAgAHUAegBzAHQAAQFzAGkAZQBzACAAYQByACAAbABlAGsAYwBpAGoA dQAgABwgYAE3AWkAdAB1AG0AcwAgAHUAbgAgACsBcwB0AGUAbgArAWIAYQAdICwAIABrAHUAcgAg AHMAdAABAXMAdAArAXMAIABwAGEAcgAgAG8AcAB0AGkAawBhAHMAIABpAGwAawF6AGkAagABAW0A IAB1AG4AIABjAGkAdAABAW0AIABwAGEAcgABAWQAKwFiAAEBbQAsACAAawBhAHMAIABzAGEAaQBz AHQAKwF0AGEAcwAgAGEAcgAgAG8AcAB0AGkAawB1ACAAdQBuACAAcgBlAGQAegBpAC4ADQBQAHIA bwBmAC4AIABBAG4AZAByAGUAagBzACAAQwATAWIAZQByAHMAIABzAHQAAQFzAHQAKwFzACAAcABh AHIAIABtAGEAZwBuABMBdABpAHMAawBvACAAYgBhAGsAdAATAXIAaQBqAHUAIABwAGkAZQBsAGkA ZQB0AG8AagB1AG0AdQAsACAAdQB6AGIAawF2AGkALAAgAGQAYQByAGIAKwFiAGEAcwAgAHAAcgBp AG4AYwBpAHAAaQBlAG0ALgANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAANgsA AAAQAADKEQAAzBEAAM4RAAAyEgAAdhIAAM4TAAASFAAA/Pr89vz28vbuAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAYWaHEWDgAABhZonDXIAAAGFmhUZYUAAANVCAEGFmh9PrcACQAIAAAyCAAA fAgAAPAIAAB0CgAANgsAAM4QAADOEQAAeBIAAM4TAAASFAAA+gAAAAAAAAAAAAAAAPoAAAAAAAAA AAAAAAD6AAAAAAAAAAAAAAAA+gAAAAAAAAAAAAAAAPoAAAAAAAAAAAAAAAD6AAAAAAAAAAAAAAAA +gAAAAAAAAAAAAAAAPoAAAAAAAAAAAAAAAD1AAAAAAAAAAAAAAAA9QAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAZ2RUZYUAAAQAAGdkfT63AAAKSQBsAGcAbwBu AGkAcwAgAFYAaQBsAGsAcwAgAHMAdAABAXMAdAArAXMAIABwAGEAcgAgAGEAcABzAHQAAQFrADwB aQBlAG0AIABrAG8AcwBtAG8AcwABASwAIABpAGUAcwBwABMBagBhAG0AAQFzACAAZAB6ACsBdgAr AWIAYQBzACAAcABhAHMAdAABAXYAEwFhAWEAbgBhAHMAIAB2AGkAZQB0AAEBbQAgAHUAbgAgAG4A bwBzAGEAYwArAWoAdQBtAGkAZQBtAC4ADQBKAHUAcgBpAHMAIABgAXQAZQBpAG4AYgBlAHIAZwBz ACAAcwB0AAEBcwB0ACsBcwAgAHAAYQByACAAcABhAGEBbABhAGkAawAgAHAAYQBzAGEAdQBsABMB IAAcIHQAbwBwAAEBHSAgAGUAcwBvAGEBYQBqAAEBbQAgAHAAbwBsAGkAbQATAXoAZQBzACAANwET AWQAEwFtACAAdQBuACAAcwBrAGEAaQBkAHIAbwBzACwAIABrAGEAcwAgAHQAAQFzACAAaQByACAA dQBuACAAawABAXAAEwFjACAAdAABAXMAIABpAHIAIAB0AGkAawAgAGwAYQBiAGEAcwAuAA0ATABl AGsAYwBpAGoAYQBzACAAaQByACAAYgBlAHoAIABtAGEAawBzAGEAcwAhACAAQQB0AG4AAQFjAGkA ZQB0ACAAdQBuACAAaQB6AGcAbAArAXQAbwBqAGEAdABpAGUAcwAhACAATABlAGsAYwBpAGoAYQBz ACAAbgBvAHQAaQBrAHMAIABuAG8AIAAxADUAOgAwADAAIAATICAAMQA5ADoAMAAwAC4ADQBMAGkA ZQBsAHMAIABwAGEAbABkAGkAZQBzACAAbQBrAXMAdQAgAGEAdABiAGEAbABzAHQAKwF0AAEBagBp AGUAbQA6ACAAUgArAWcAYQBzACAARABvAG0AZQBpACwAIABBAEMAQwBFAE4AVABVAFIARQAsACAA UgBJAE0ASQAuACAASwABASAAYQByACsBIABwAGEAbABkAGkAZQBzACAAZAByAGEAdQBnAGkAZQBt AC4AbAB2ACwAIABMAFQAVgAsACAARABJAEUATgBBACwAIAA1AG0AaQBuACwAIABLAG8AbgB0AGkA IABCAHUAcwBzACwAIABBAGwAZABhAHIAaQBzACwAIABEAG8AdQBiAGwAZQAgAEMAbwBmAGYAZQBl ACwAIABQAHIAbwBXAGUAYgAsACAAUgArAWcAYQBzACAAWgBvAG8AZAABAXIAegBzAC4ADQBWAGEA aQByAAEBawAgAGkAbgBmAG8AIABwAGEAIAB0AAEBbAByAHUAbgBpADoAIAAyADYAMwAxADYANAAx ADMALgANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMgAxkGgBOnBsQZcA H7DQLyCw4D0hsAgHIrAIByOQoAUkkKAFJbAAABewxAIYsMQCDJDEAgAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqBA8AEgABAAsBDwAH AAMAAwADAAAABAAIAAAAmAAAAJ4AAACeAAAAngAAAJ4AAACeAAAAngAAAJ4AAACeAAAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAdgIAAHYCAAB2AgAAdgIAAHYCAAB2AgAAdgIA AHYCAAB2AgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAPgIAADYGAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAAKgAAAA2BgAANgYAABYAAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAALgAAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAABoAQAASAEAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAA 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 AAAAAAAAAAAAAAAAFAIAAHRoZW1lL3RoZW1lL3RoZW1lTWFuYWdlci54bWxQSwECLQAUAAYACAAA ACEAlrWt4pYGAABQGwAAFgAAAAAAAAAAAAAAAADRAgAAdGhlbWUvdGhlbWUvdGhlbWUxLnhtbFBL AQItABQABgAIAAAAIQAN0ZCftgAAABsBAAAnAAAAAAAAAAAAAAAAAJsJAAB0aGVtZS90aGVtZS9f cmVscy90aGVtZU1hbmFnZXIueG1sLnJlbHNQSwUGAAAAAAUABQBdAQAAlgoAAAAAPD94bWwgdmVy c2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pg0KPGE6Y2xyTWFw IHhtbG5zOmE9Imh0dHA6Ly9zY2hlbWFzLm9wZW54bWxmb3JtYXRzLm9yZy9kcmF3aW5nbWwvMjAw Ni9tYWluIiBiZzE9Imx0MSIgdHgxPSJkazEiIGJnMj0ibHQyIiB0eDI9ImRrMiIgYWNjZW50MT0i YWNjZW50MSIgYWNjZW50Mj0iYWNjZW50MiIgYWNjZW50Mz0iYWNjZW50MyIgYWNjZW50ND0iYWNj ZW50NCIgYWNjZW50NT0iYWNjZW50NSIgYWNjZW50Nj0iYWNjZW50NiIgaGxpbms9ImhsaW5rIiBm b2xIbGluaz0iZm9sSGxpbmsiLz4AAAAApAMAABMAABYAAAUA/////wAIAAASFAAABgAAAAAIAAAS FAAABwAAAA8AAPA4AAAAAAAG8BgAAAACCAAAAgAAAAEAAAABAAAAAQAAAAIAAABAAB7xEAAAAP// AAAAAP8AgICAAPcAABAADwAC8JIAAAAQAAjwCAAAAAEAAAABBAAADwAD8DAAAAAPAATwKAAAAAEA CfAQAAAAAAAAAAAAAAAAAAAAAAAAAAIACvAIAAAAAAQAAAUAAAAPAATwQgAAABIACvAIAAAAAQQA AAAOAABTAAvwHgAAAL8BAAAQAMsBAAAAAP8BAAAIAAQDCQAAAD8DAQABAAAAEfAEAAAAAQAAAAAA AAAZAAAAJAAAACUAAAAvAAAAMAAAADUAAAA2AAAAPQAAAEsAAABWAAAAVwAAAFsAAABdAAAAYgAA AGMAAABoAAAAaQAAAG4AAABvAAAAdgAAAHgAAACAAAAAgQAAAIgAAACJAAAAkQAAAJ8AAACoAAAA qQAAAK0AAACvAAAAtgAAALcAAADAAAAAwQAAAMMAAADEAAAAywAAAM0AAADUAAAA2AAAAOAAAADj AAAA5gAAAOcAAADuAAAA8wAAAPoAAAD7AAAAAwEAAAcBAAAMAQAADQEAABYBAAAYAQAAGwEAABwB AAAlAQAAJgEAACgBAAApAQAALwEAADMBAAA4AQAAQAEAAEcBAABIAQAATgEAAE8BAABWAQAAWwEA AGUBAABmAQAAbwEAAHABAAB8AQAAfgEAAIQBAACGAQAAjgEAAI8BAACZAQAAmwEAAKIBAACjAQAA qAEAAKkBAACwAQAAtQEAAL8BAADAAQAAxwEAAMkBAADTAQAA1AEAANwBAADdAQAA6QEAAOoBAADw AQAA9AEAAAACAAACAgAABwIAAAgCAAASAgAAEwIAABoCAAAfAgAAJgIAACcCAAAuAgAAMAIAADQC AAA2AgAAPgIAAD8CAABIAgAASQIAAE4CAABSAgAAWgIAAFwCAABfAgAAYAIAAGMCAABkAgAAZgIA AGoCAABvAgAAcAIAAHMCAAB0AgAAdgIAAHcCAAB6AgAAewIAAIACAACCAgAAigIAAIsCAACNAgAA jgIAAJECAACSAgAAmAIAAJoCAACiAgAApgIAALMCAAC1AgAAvQIAAL4CAADEAgAA1wIAANwCAADd AgAA5AIAAOUCAADpAgAA6gIAAPgCAAD6AgAA/wIAAAADAAAFAwAAGAMAABoDAAAbAwAAHgMAAB8D AAAmAwAARgMAAEsDAABSAwAAWQMAAGoDAABwAwAAcgMAAHcDAAB4AwAAgAMAAIIDAACIAwAAkQMA AJgDAACaAwAAogMAAKYDAAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwA BwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAH ABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcA HAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAc AAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwA BwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAH ABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHABwABwAcAAcAHAAHAAQABwAAAAAA BAAAAAkAAABAAAAASgAAAAQBAAAGAQAA8QEAAPMBAABPAgAAUQIAAKMCAAClAgAAtQIAANYCAACC AwAAowMAAKYDAAAHADMABwAzAAcAMwAHADMABwAzAAcAMwAHADMABwAEAAcAAAAAAGECAADWAgAA 1wIAAKMDAACmAwAAAwAEAAMABAAHAAcAAAAEAAAACAAAAOUAAAAAAAAABAAAAHEWDgAvfRUA3lJi AFRlhQBsQZcAfT63AJw1yAAAAAAApAMAAKYDAAAAAAAAAQAAAP9AAYABAKIDAACiAwAAAEj8BbsA uwCiAwAAAAAAAKIDAAAAAAAAAhwAAAAAAAAAmwEAAKQDAACYAAAIAAAAAJgAABAAAAAA//8BAAAA BwBVAG4AawBuAG8AdwBuAP//AQAIAAAAAAAAAAAAAAD//wEAAAAAAP//AAACAP//AAAAAP//AAAC AP//AAAAAAUAAABHHpABugACAgYDBQQFAgMEhyoAIAAAAIAIAAAAAAAAAP8BAAAAAAAAVABpAG0A ZQBzACAATgBlAHcAIABSAG8AbQBhAG4AAAA1HpABAgAFBQECAQcGAgUHAAAAAAAAABAAAAAAAAAA AAAAAIAAAAAAUwB5AG0AYgBvAGwAAAAzLpABugACCwYEAgICAgIEhyoAIAAAAIAIAAAAAAAAAP8B AAAAAAAAQQByAGkAYQBsAAAANy6QAboAAg8FAgICBAMCBO8CAKB7IABAAAAAAAAAAACfAAAAAAAA AEMAYQBsAGkAYgByAGkAAABBHpABugAAAAAAAAAAAAAA7wIAoOsgAEIAAAAAAAAAAJ8AAAAAAAAA QwBhAG0AYgByAGkAYQAgAE0AYQB0AGgAAAAiAAQAcQiIGADw0AIAAGgBAAAAAKJ0uaazdLmmAAAA AAMAEQAAAIsAAAAZAwAAAQABAAAABAADkAYAAACLAAAAGQMAAAEAAQAAAAYAAAAAAAAAIQMA8BAA AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAegBbQAtACBgTIwAAAAAAAAAAAAAAAAAACjAwAA owMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAIAAAAAAAAAAAAAMoNRAPAQAAgA/P0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhQ AAAAAAnw/w8BCCRQAADpBAAA////f////3////9/////f////3////9/////f30+twAABAAAMgAA AAAAAAAAAAAAAAAAAAAAAAAAACEEAAAAAAAAAAAAAAAAAAAAAAAAEBwAAAQAAAAAAAAAAAB4AAAA eAAAAAAAAAAAAAAAoAUAAP//EgAAAAAAAAAAAAAAAAAAAAMAQQBBAEEAAwBBAEEAQQAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+/wAABQECAAAAAAAAAAAAAAAAAAAAAAABAAAA 4IWf8vlPaBCrkQgAKyez2TAAAABgAQAAEQAAAAEAAACQAAAAAgAAAJgAAAADAAAApAAAAAQAAACw AAAABQAAALwAAAAGAAAAyAAAAAcAAADUAAAACAAAAOQAAAAJAAAA8AAAABIAAAD8AAAACgAAABwB AAAMAAAAKAEAAA0AAAA0AQAADgAAAEABAAAPAAAASAEAABAAAABQAQAAEwAAAFgBAAACAAAA6QQA AB4AAAAEAAAAAAAAAB4AAAAEAAAAAAAAAB4AAAAEAAAAQUFBAB4AAAAEAAAAAAAAAB4AAAAEAAAA AAAAAB4AAAAIAAAATm9ybWFsAAAeAAAABAAAAEFBQQAeAAAABAAAADMAAAAeAAAAGAAAAE1pY3Jv c29mdCBPZmZpY2UgV29yZAAAAEAAAAAApvdfAgAAAEAAAAAAZOGr5PbHAUAAAAAACtkL5/bHAQMA AAABAAAAAwAAAIsAAAADAAAAGQMAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v8AAAUBAgAAAAAAAAAAAAAAAAAAAAAAAQAAAALVzdWcLhsQ k5cIACss+a4wAAAA6AAAAAwAAAABAAAAaAAAAA8AAABwAAAABQAAAHwAAAAGAAAAhAAAABEAAACM AAAAFwAAAJQAAAALAAAAnAAAABAAAACkAAAAEwAAAKwAAAAWAAAAtAAAAA0AAAC8AAAADAAAAMkA AAACAAAA6QQAAB4AAAAEAAAAQUEAAAMAAAAGAAAAAwAAAAEAAAADAAAAowMAAAMAAAAAAAwACwAA AAAAAAALAAAAAAAAAAsAAAAAAAAACwAAAAAAAAAeEAAAAQAAAAEAAAAADBAAAAIAAAAeAAAABgAA AFRpdGxlAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAA /v///w0AAAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABkAAAAa AAAA/v///xwAAAAdAAAAHgAAAB8AAAAgAAAAIQAAACIAAAD+////JAAAACUAAAAmAAAAJwAAACgA AAApAAAAKgAAAP7////9////LQAAAP7////+/////v////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ////////////////UgBvAG8AdAAgAEUAbgB0AHIAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAABYABQH//////////wMAAAAGCQIAAAAAAMAAAAAAAABGAAAAAAAA AAAAAAAAUByOEuf2xwEvAAAAgAAAAAAAAAAxAFQAYQBiAGwAZQAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgACAf////8FAAAA/////wAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAC3HQAAAAAAAFcAbwByAGQARABvAGMAdQBt AGUAbgB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAIBAQAAAP// ////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQWAAAAAAAABQBT AHUAbQBtAGEAcgB5AEkAbgBmAG8AcgBtAGEAdABpAG8AbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAACgAAgECAAAABAAAAP////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAb AAAAABAAAAAAAAAFAEQAbwBjAHUAbQBlAG4AdABTAHUAbQBtAGEAcgB5AEkAbgBmAG8AcgBtAGEA dABpAG8AbgAAAAAAAAAAAAAAOAACAf///////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAACMAAAAAEAAAAAAAAAEAQwBvAG0AcABPAGIAagAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAIA////////////////AAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////// //////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAP///////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAEAAAD+//////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ////////////AQD+/wMKAAD/////BgkCAAAAAADAAAAAAAAARicAAABNaWNyb3NvZnQgT2ZmaWNl IFdvcmQgOTctMjAwMyBEb2N1bWVudAAKAAAATVNXb3JkRG9jABAAAABXb3JkLkRvY3VtZW50LjgA 9DmycQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAA= ------_=_NextPart_002_01C7F6E7.540B3540-- ------_=_NextPart_000_01C7F6E7.540B3540-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_12_soft.txt.eml000066400000000000000000000032541502127241700257020ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs22976ugm; Sat, 15 Sep 2007 00:47:40 -0700 (PDT) Received: by 10.35.36.13 with SMTP id o13mr3311239pyj.1189842459008; Sat, 15 Sep 2007 00:47:39 -0700 (PDT) Return-Path: <> Received: by 10.35.36.13 with SMTP id o13mr4245718pyj; Sat, 15 Sep 2007 00:47:39 -0700 (PDT) Message-ID: <00163600cfda043a27c91b990c7fbc@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Delay) Date: Sat, 15 Sep 2007 00:47:39 -0700 (PDT) This is an automatically generated Delivery Status Notification THIS IS A WARNING MESSAGE ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. Delivery to the following recipient has been delayed: info@radioliepaja.lv Message will be retried for 2 more day(s) Technical details of temporary failure: TEMP_FAILURE: Could not initiate SMTP conversation with any hosts: [radioliepaja.lv. (50): Connection timed out] ----- Message header follows ----- Received: by 10.35.36.13 with SMTP id o13mr1865462pyj.1189751181221; Thu, 13 Sep 2007 23:26:21 -0700 (PDT) Received: by 10.35.73.12 with HTTP; Thu, 13 Sep 2007 23:26:20 -0700 (PDT) Message-ID: Date: Fri, 14 Sep 2007 09:26:20 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com, "=?ISO-8859-13?Q?J=E2nis_Erts?=" Subject: =?UTF-8?Q?15.septembr=C4=AB_neaizmirstams_pas=C4=81kums!?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_979_8743786.1189751181201" ----- Message body suppressed ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_13.txt.eml000066400000000000000000001326101502127241700246470ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.35.73.12 with SMTP id a12cs13488pyl; Thu, 13 Sep 2007 23:26:29 -0700 (PDT) Received: by 10.66.184.17 with SMTP id h17mr3414064ugf.1189751188285; Thu, 13 Sep 2007 23:26:28 -0700 (PDT) Return-Path: <> Received: from LVAEI-EXCH.lvaei.lv (mail.lvaei.lv [217.199.123.22]) by mx.google.com with ESMTP id h7si1530964nfh.2007.09.13.23.26.27; Thu, 13 Sep 2007 23:26:28 -0700 (PDT) Received-SPF: pass (google.com: domain of LVAEI-EXCH.lvaei.lv designates 217.199.123.22 as permitted sender) client-ip=217.199.123.22; Authentication-Results: mx.google.com; spf=pass smtp.mail= From: postmaster@lvaei.lv To: agris.ameriks@gmail.com Date: Fri, 14 Sep 2007 09:46:02 +0300 MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="9B095B5ADSN=_01C7DED0EB082C7600015D0ALVAEI?EXCH.lvaei" X-DSNContext: 7ce717b1 - 1158 - 00000002 - 00000000 Message-ID: Subject: Delivery Status Notification (Failure) This is a MIME-formatted message. Portions of this message may be unreadable without a MIME-capable mail program. --9B095B5ADSN=_01C7DED0EB082C7600015D0ALVAEI?EXCH.lvaei Content-Type: text/plain; charset=unicode-1-1-utf-7 This is an automatically generated Delivery Status Notification. Delivery to the following recipients failed. ilzeB@lvaei.lv --9B095B5ADSN=_01C7DED0EB082C7600015D0ALVAEI?EXCH.lvaei Content-Type: message/delivery-status Reporting-MTA: dns;LVAEI-EXCH.lvaei.lv Received-From-MTA: dns;py-out-1112.google.com Arrival-Date: Fri, 14 Sep 2007 09:46:01 +0300 Final-Recipient: rfc822;ilzeB@lvaei.lv Action: failed Status: 5.2.2 X-Display-Name: Ilze Bumane --9B095B5ADSN=_01C7DED0EB082C7600015D0ALVAEI?EXCH.lvaei Content-Type: message/rfc822 Received: from py-out-1112.google.com ([64.233.166.181]) by LVAEI-EXCH.lvaei.lv with Microsoft SMTPSVC(6.0.3790.1830); Fri, 14 Sep 2007 09:46:01 +0300 Received: by py-out-1112.google.com with SMTP id p76so1664020pyb for ; Thu, 13 Sep 2007 23:26:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type; bh=UkLIOLa7aKX8l2Co4lQQH3S/oEkmFQxnABs/uYG4J8E=; b=dhMd24LoSESYtapU28YxUj4cjlIH2FOLv3hAE6gBF/lf0FBNyhyKdAMOBd0BkaexqcRMBppWNOT/QgWReqjOXFj0fUKhR9scXK+HwKdYmHm1VVOOlKsdBTEvWKWTSJJ8f+TYkMaSbqXDvosZFJR903KYEw3sTPkP1+TDZmeXcao= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type; b=rJxPO+Tb+m374iu5R+K/cpKZcmXfs59lcztuwmbzM7Vdh5LVGb9P5pTbn8xlgyw0WH7iqUXGUWZxjJlQu+gbOwTHjcvKkcxoPeEXAlaW1XNma/PVQ30cC0BSmsDqZh12t1P3lqksap5ZgF1yTlsm+yZq6TiypiBYTa3L2uEEGvw= Received: by 10.35.36.13 with SMTP id o13mr1865462pyj.1189751181221; Thu, 13 Sep 2007 23:26:21 -0700 (PDT) Received: by 10.35.73.12 with HTTP; Thu, 13 Sep 2007 23:26:20 -0700 (PDT) Message-ID: Date: Fri, 14 Sep 2007 09:26:20 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com, "=?ISO-8859-13?Q?J=E2nis_Erts?=" Subject: =?UTF-8?Q?15.septembr=C4=AB_neaizmirstams_pas=C4=81kums!?= MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_979_8743786.1189751181201" Return-Path: agris.ameriks@gmail.com X-OriginalArrivalTime: 14 Sep 2007 06:46:01.0247 (UTC) FILETIME=[E9DF4AF0:01C7F69A] ------=_Part_979_8743786.1189751181201 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCkluZm9ybcSTamFtIErFq3MgcGFyIGdyYW5kaW96dSBzdHVkZW50dSBwYXPEgWt1 bXUgUsSrZ2FzIGNlbnRyxIEgLSBFc3BsYW7EgWTEkyEKCjE1LnNlcHRlbWJyxKsgUsSrZ8SBLCBF c3BsYW7EgWTEkywgbm90aWtzIG5lYWl6bWlyc3RhbXMgcGFzxIFrdW1zIGphdW5pZcWhaWVtCuKA kyBTdHVkZW50dSBQxIFydGlqcyEKClV6c3TEgXNpZXMgcG9wdWzEgXJhcyBncnVwYXMsIGvEgSBD UkVETywgTW9ybmluZyBBZnRlciwgTmV3T2xkLApTb3VuZEFyY2FkZSwgQ2Fjb3Bob25pY3MsIGvE gSBhcsSrIGltcHJvdml6xIFjaWphcyB0ZcSBdHJpcyBIYW1sZXRzIHVuIGLFq3MKaWVzcMSTamEg dsSTcm90LCBrxIEgZHppZXNtdSBkdWVsxKsgc2FjZW7FoWFzIHJla3RvcmkgdW4gc3R1ZGVudGku ClV6c3TEgXNpZXMgYXLEqyB2aWpvbG5pZWNlIENyaXNDYW50by4KClBhcmvEgSBub3Rpa3Mgc2Vt aW7EgXJzLCBrdXJhIGlldHZhcm9zIHZhcsSTcyBub2tsYXVzxKt0aWVzIGd1ZHJ1IGNpbHbEk2t1 Cmxla2NpamFzOiBwcm9mLiBBbmRyZWpzIEPEk2JlcnMsIHByb2YuIEl2YXJzIEzEgWNpcywgSnVy aXMgxaB0ZWluYmVyZ3MsCk9sZ2EgVsSrdG9sacWGYSwgSWxnb25pcyBWaWxrcyB1LmMuCgpNxJNy xLdpcyAtIG9yZ2FuaXrEk3QgcGFzxIFrdW11IHN0dWRlbnRpZW0sIGt1ciBqYXVuaWXFoWkgdmFy xJNzIGRhdWR6cHVzxKtnaQpwYXZhZMSrdCBsYWlrdSwgdHVya2zEgXQsIHBhciBicsSrdnUuIFN0 dWRlbnRpZW0gdGlrcyBwaWVkxIF2xIF0cyB2xJNyb3QKbXV6aWvEgWx1cyB1biB0ZWF0csSBbHVz IHByaWVrxaFuZXN1bXVzLCBwYXBpbGRpbsSBdCBzYXZhcyB6aW7EgcWhYW5hcwphcG1la2zEk2pv dCBkYcW+xIFkdXMgc2VtaW7EgXJ1cyB1biBsZWtjaWphcyBwYXIgemluxIF0bmksIHBpZWRhbMSr dGllcwpkYcW+xIFkxIFzIGFrdGl2aXTEgXTEk3MgdW4gc3DEk2zEk3MgKHN0YWZldGVzLCDFoWFo YSB0dXJuxKtycywgQUNDRU5UVVJFCnJvZGVvLCBSSU1JIGtsaW7FoWvEgXDEk2p1IHNpZW5hIHUu Yy4pLCBnxat0IHByYWt0aXNrYXMgaWVtYcWGYXMgc2Fsc2FzCm5vZGFyYsSrYsSBcywga8SBIGFy xKsgdsSTcm9qb3QgamF1bm9zIG3EgWtzbGluaWVrdXMgbm8gTE1BIHZpxYZ1IHJhZG/FoWFqxIEK ZGFyYsSBLCBrdXIgdmFyxJNzIGllc2Fpc3TEq3RpZXMgYXLEqyBza2F0xKt0xIFqaS4gU3R1ZGVu dGllbSBixatzIGl6ZGV2xKtiYQppZXBhesSrdGllcyB1biBpZXNhaXN0xKt0aWVzIG9yZ2FuaXrE gWNpasSBcy4gVmlzYSBwYXPEgWt1bWEgbGFpa8SBCmFwbWVrbMSTdMSBamllbSBixatzIGllc3DE k2phIHBhbMSrZHrEk3QgIk1hcnNhIGdhdHZlcyIga3LEq3plcyBjZW50cmFtIGFyCm5hdWRpxYZ1 LCBhcMSjxJNyYnUgdmFpIGVsZWt0cm9uaWt1LgoKVU4gVEFTIFZJU1MgQkVaIE1BS1NBUyEKCk5v c2zEk2d1bcSBIHBhc8SBa3VtYSBhcG1la2zEk3TEgWppIHZhcsSTcyBsYWltxJN0IG1vYmlsbyB0 ZWxlZm9udSBOT0tJQSA2MzAwCnVuIHBvcnRhdMSrdm8gZGF0b3J1IQoKVmllbsSBIHRlaWt1bcSB IHZhciB0ZWlrdCwga2Eg4oCeU3R1ZGVudHUgUMSAUlRJSlMhIiBpciBtdXppa8SBbGkgdW4KdGVh dHLEgWxpIGF0cmFrdMSrdmkgYWt0xKt2cyB1biBpbnRlbGVrdHXEgWxpIHVuIGluZm9ybWF0xKt2 aSBpemdsxKt0b2pvxaFzCmxhYmRhcsSrYmFzIHBhc8SBa3VtcyBqYXVuaWXFoWllbS4KClBhc8SB a3VtxIEgcGllZGFsxKtzaWVzIGFyxKsgxaHEgWRhcyBvcmdhbml6xIFjaWphczogU3R1ZGVudHUg a29vcnBvcsSBY2lqdQphcHZpZW7Eq2JhLCBCaWVkcsSrYmFzICJMYXR2aWphcyBNYXpwdWxraSIs IExhdHZpamFzIEtyaXN0xKtnxIEgU3R1ZGVudHUKYnLEgWzEq2JhLCBBZ2FwZSBMYXR2aWphIHUu Yy4KCkxpZWxzIHBhbGRpZXMgbcWrc3UgYXRiYWxzdMSrdMSBamllbTogUsSrZ2FzIERvbWVpLCBB Q0NFTlRVUkUsIFJJTUkuIEvEgQphcsSrIHBhbGRpZXMgZHJhdWdpZW0ubHYsIExUViwgRElFTkEs IDVtaW4sIEtvbnRpIEJ1c3MsIEFsZGFyaXMsIERvdWJsZQpDb2ZmZWUsIFByb1dlYiwgUsSrZ2Fz IFpvb2TEgXJ6cy4KClZhaXLEgWsgaW5mbyB3d3cuc3R1ZGVudHBhcnR5Lmx2LgoKMTQuMDkuMjAw NwoKCgpTYWdhdGF2b2phClNhbmRhIEJsb21rYWxuYQoKCi0tIApBciBjaWXFhnUsCkFncmlzIEFt ZXJpa3MKTW9iLnRlbC4gMiA2NDYxMTAxCkUtcGFzdHM6IEFncmlzLkFtZXJpa3NAZ21haWwuY29t Cg== ------=_Part_979_8743786.1189751181201 Content-Type: application/msword; name="PR_SP.doc" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="PR_SP.doc" X-Attachment-Id: f_f6kb3mdz 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAANAAAAAAAAAAA EAAANgAAAAEAAAD+////AAAAADMAAAD///////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////s pcEAW4AJBAAA8BK/AAAAAAAAEAAAAAAACAAAUhwAAA4AYmpiaqz6rPoAAAAAAAAAAAAAAAAAAAAA AAAJBBYALiIAAM6QAQDOkAEA6gcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAA AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAALcAAAAAAAAGAAAAAAAAAAYAAEMT AAAAAAAAQxMAAAAAAABDEwAAAAAAAEMTAAAAAAAAQxMAABQAAAAAAAAAAAAAAP////8AAAAAVxMA AAAAAABXEwAAAAAAAFcTAAAAAAAAVxMAABQAAABrEwAAFAAAAFcTAAAAAAAA9xwAAPgAAAB/EwAA FgAAAJUTAAAAAAAAlRMAAAAAAACVEwAAAAAAAJUTAAAAAAAAcBQAAB4AAACOFAAADAAAAJoUAAAI AAAAahwAAAIAAABsHAAAAAAAAGwcAAAAAAAAbBwAAAAAAABsHAAAAAAAAGwcAAAAAAAAbBwAACQA AADvHQAAogIAAJEgAAB2AAAAkBwAACEAAAAAAAAAAAAAAAAAAAAAAAAAQxMAAAAAAACiFAAAAAAA AAAAAAAAAAAAAAAAAAAAAABwFAAAAAAAAHAUAAAAAAAAohQAAAAAAACiFAAAAAAAAJAcAAAAAAAA AAAAAAAAAABDEwAAAAAAAEMTAAAAAAAAlRMAAAAAAAAAAAAAAAAAAJUTAADbAAAAsRwAABYAAACG GQAAAAAAAIYZAAAAAAAAhhkAAAAAAACiFAAAwgIAAEMTAAAAAAAAlRMAAAAAAABDEwAAAAAAAJUT AAAAAAAAahwAAAAAAAAAAAAAAAAAAIYZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAohQAAAAAAABqHAAAAAAAAAAAAAAAAAAAhhkAAAAAAACGGQAA HgAAAK4bAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1hsAAAAAAACVEwAAAAAAAP////8AAAAAsLTM6Zf2 xwEAAAAAAAAAAFcTAAAAAAAAZBcAAOgAAADGGwAACAAAAAAAAAAAAAAAVhwAABQAAADHHAAAMAAA APccAAAAAAAAzhsAAAgAAAAHIQAAAAAAAEwYAACaAAAAByEAABAAAADWGwAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW GwAAFAAAAAchAAAAAAAAAAAAAAAAAABDEwAAAAAAAOobAABsAAAAohQAAAAAAACiFAAAAAAAAIYZ AAAAAAAAohQAAAAAAACiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAohQA AAAAAACiFAAAAAAAAKIUAAAAAAAAkBwAAAAAAACQHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAA5hgAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKIUAAAA AAAAohQAAAAAAACiFAAAAAAAAPccAAAAAAAAohQAAAAAAACiFAAAAAAAAKIUAAAAAAAAohQAAAAA AAAAAAAAAAAAAP////8AAAAA/////wAAAAD/////AAAAAAAAAAAAAAAA/////wAAAAD/////AAAA AP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA /////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAAAchAAAAAAAAohQAAAAAAACi FAAAAAAAAKIUAAAAAAAAohQAAAAAAACiFAAAAAAAAKIUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiFAAAAAAAAKIUAAAAAAAAohQA AAAAAAAABgAACQwAAAkSAAA6AQAABQASAQAACQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFMAdAB1 AGQAZQBuAHQAdQAgAFAAAAFSAFQASQBKAFMADQAyADAAMAA3AC4AIABnAGEAZABhACAAMQA1AC4A cwBlAHAAdABlAG0AYgByACsBDQBFAHMAcABsAGEAbgABAWQAEwENAA0ADQAxADUALgBzAGUAcAB0 AGUAbQBiAHIAKwEgAFIAKwFnAAEBLAAgAEUAcwBwAGwAYQBuAAEBZAATASwAIABuAG8AdABpAGsA cwAgAG4AZQBhAGkAegBtAGkAcgBzAHQAYQBtAHMAIABwAGEAcwABAWsAdQBtAHMAIABqAGEAdQBu AGkAZQBhAWkAZQBtACAAEyAgAFMAdAB1AGQAZQBuAHQAdQAgAFAAAQFyAHQAaQBqAHMAIQANAA0A VQB6AHMAdAABAXMAaQBlAHMAIABwAG8AcAB1AGwAAQFyAGEAcwAgAGcAcgB1AHAAYQBzACwAIABr AAEBIABDAFIARQBEAE8ALAAgAE0AbwByAG4AaQBuAGcAIABBAGYAdABlAHIALAAgAE4AZQB3AE8A bABkACwAIABTAG8AdQBuAGQAQQByAGMAYQBkAGUALAAgAEMAYQBjAG8AcABoAG8AbgBpAGMAcwAs ACAAawABASAAYQByACsBIABpAG0AcAByAG8AdgBpAHoAAQFjAGkAagBhAHMAIAB0AGUAAQF0AHIA aQBzACAASABhAG0AbABlAHQAcwAgAHUAbgAgAGIAawFzACAAaQBlAHMAcAATAWoAYQAgAHYAEwFy AG8AdAAsACAAawABASAAZAB6AGkAZQBzAG0AdQAgAGQAdQBlAGwAKwEgAHMAYQBjAGUAbgBhAWEA cwAgAHIAZQBrAHQAbwByAGkAIAB1AG4AIABzAHQAdQBkAGUAbgB0AGkALgAgAFUAegBzAHQAAQFz AGkAZQBzACAAYQByACsBIAB2AGkAagBvAGwAbgBpAGUAYwBlACAAQwByAGkAcwBDAGEAbgB0AG8A LgANAA0AUABhAHIAawABASAAbgBvAHQAaQBrAHMAIABzAGUAbQBpAG4AAQFyAHMALAAgAGsAdQBy AGEAIABpAGUAdAB2AGEAcgBvAHMAIAB2AGEAcgATAXMAIABuAG8AawBsAGEAdQBzACsBdABpAGUA cwAgAGcAdQBkAHIAdQAgAGMAaQBsAHYAEwFrAHUAIABsAGUAawBjAGkAagBhAHMAOgAgAHAAcgBv AGYALgAgAEEAbgBkAHIAZQBqAHMAIABDABMBYgBlAHIAcwAsACAAcAByAG8AZgAuACAASQB2AGEA cgBzACAATAABAWMAaQBzACwAIABKAHUAcgBpAHMAIABgAXQAZQBpAG4AYgBlAHIAZwBzACwAIABP AGwAZwBhACAAVgArAXQAbwBsAGkARgFhACwAIABJAGwAZwBvAG4AaQBzACAAVgBpAGwAawBzACAA dQAuAGMALgANAA0ATQATAXIANwFpAHMAIAAtACAAbwByAGcAYQBuAGkAegATAXQAIABwAGEAcwAB AWsAdQBtAHUAIABzAHQAdQBkAGUAbgB0AGkAZQBtACwAIABrAHUAcgAgAGoAYQB1AG4AaQBlAGEB aQAgAHYAYQByABMBcwAgAGQAYQB1AGQAegBwAHUAcwArAWcAaQAgAHAAYQB2AGEAZAArAXQAIABs AGEAaQBrAHUALAAgAHQAdQByAGsAbAABAXQALAAgAHAAYQByACAAYgByACsBdgB1AC4AIABTAHQA dQBkAGUAbgB0AGkAZQBtACAAdABpAGsAcwAgAHAAaQBlAGQAAQF2AAEBdABzACAAdgATAXIAbwB0 ACAAbQB1AHoAaQBrAAEBbAB1AHMAIAB1AG4AIAB0AGUAYQB0AHIAAQFsAHUAcwAgAHAAcgBpAGUA awBhAW4AZQBzAHUAbQB1AHMALAAgAHAAYQBwAGkAbABkAGkAbgABAXQAIABzAGEAdgBhAHMAIAB6 AGkAbgABAWEBYQBuAGEAcwAgAGEAcABtAGUAawBsABMBagBvAHQAIABkAGEAfgEBAWQAdQBzACAA cwBlAG0AaQBuAAEBcgB1AHMAIAB1AG4AIABsAGUAawBjAGkAagBhAHMAIABwAGEAcgAgAHoAaQBu AAEBdABuAGkALAAgAHAAaQBlAGQAYQBsACsBdABpAGUAcwAgAGQAYQB+AQEBZAABAXMAIABhAGsA dABpAHYAaQB0AAEBdAATAXMAIAB1AG4AIABzAHAAEwFsABMBcwAgACgAcwB0AGEAZgBlAHQAZQBz ACwAIABhAWEAaABhACAAdAB1AHIAbgArAXIAcwAsACAAQQBDAEMARQBOAFQAVQBSAEUAIAByAG8A ZABlAG8ALAAgAFIASQBNAEkAIABrAGwAaQBuAGEBawABAXAAEwFqAHUAIABzAGkAZQBuAGEAIAB1 AC4AYwAuACkALAAgAGcAawF0ACAAcAByAGEAawB0AGkAcwBrAGEAcwAgAGkAZQBtAGEARgFhAHMA IABzAGEAbABzAGEAcwAgAG4AbwBkAGEAcgBiACsBYgABAXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAACAIAAAi CAAAaAgAAPwIAAAeCQAAIAkAAFYJAABgCQAAjAkAAJgJAACaCQAAtAkAALYJAADOCQAA0AkAAN4J AAAMCgAAGgoAAKIKAADWCgAA6goAAOwKAADuCgAA8AoAAJALAAC4CwAAvAsAAEgMAABKDAAAYA8A AIIPAAAAFAAAUBUAAEYWAABIFgAAShYAAHoWAACQFgAAohYAAMoWAAAKFwAAMhcAADQXAAA2FwAA WhcAAGwXAABuFwAAtBcAAPz4/PTs9Ojk3fTW3dbd5NLo1ujSztLoysW7xbuz9Oyx9Kn0pMWd5J30 7JnSlIyFewAAEhVopQq8ABZopQq8ADUIgV0IgQAMFmilCrwANQiBXQiBAA8VaKUKvAAWaKUKvABd CIEJFmilCrwAXQiBBhZoumi3AAAMFWhPOIMAFmhPOIMAAAkWaE84gwBcCIEPFWilCrwAFmjpbksA NQiBA1UIAQ8VaOluSwAWaOluSwBcCIESFWhPOIMAFmjpbksANQiBXAiBAAkWaOluSwBcCIEGFmjB FFAAAAYWaDwgEAAABhZopQq8AAAMFWilCrwAFmiuAm4AAAwVaKUKvAAWaE84gwAABhZoTziDAAAG FmiuAm4AAA8VaOluSwAWaOluSwA1CIEGFmjpbksAAAYWaPpPewAABhZoszapADAACAAAIggAAFII AABmCAAAaAgAAGoIAAAgCQAAIgkAAO4KAADwCgAASgwAAEwMAABIFgAAShYAAHoWAAB8FgAANBcA ADYXAAB+GAAAgBgAAMoZAADMGQAAIhsAACQbAAC8GwAAvhsAANQbAAD9AAAAAAAAAAAAAAAA/QAA AAAAAAAAAAAAAP0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAP0AAAAAAAAA AAAAAAD9AAAAAAAAAAAAAAAA9QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAADtAAAAAAAAAAAAAAAA /QAAAAAAAAAAAAAAAO0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAP0AAAAA AAAAAAAAAAD1AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAPUAAAAAAAAAAAAAAAD1AAAAAAAAAAAA AAAA9QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAP0A AAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA5QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAADJAJh JAJnZKUKvAAABwAAAyQDYSQDZ2TpbksAAAcAAAMkA2EkA2dkpQq8AAABAAAAGiwAIABrAAEBIABh AHIAKwEgAHYAEwFyAG8AagBvAHQAIABqAGEAdQBuAG8AcwAgAG0AAQFrAHMAbABpAG4AaQBlAGsA dQBzACAAbgBvACAATABNAEEAIAB2AGkARgF1ACAAcgBhAGQAbwBhAWEAagABASAAZABhAHIAYgAB ASwAIABrAHUAcgAgAHYAYQByABMBcwAgAGkAZQBzAGEAaQBzAHQAKwF0AGkAZQBzACAAYQByACsB IABzAGsAYQB0ACsBdAABAWoAaQAuACAAUwB0AHUAZABlAG4AdABpAGUAbQAgAGIAawFzACAAaQB6 AGQAZQB2ACsBYgBhACAAaQBlAHAAYQB6ACsBdABpAGUAcwAgAHUAbgAgAGkAZQBzAGEAaQBzAHQA KwF0AGkAZQBzACAAbwByAGcAYQBuAGkAegABAWMAaQBqAAEBcwAuACAAVgBpAHMAYQAgAHAAYQBz AAEBawB1AG0AYQAgAGwAYQBpAGsAAQEgAGEAcABtAGUAawBsABMBdAABAWoAaQBlAG0AIABiAGsB cwAgAGkAZQBzAHAAEwFqAGEAIABwAGEAbAArAWQAegATAXQAIAAiAE0AYQByAHMAYQAgAGcAYQB0 AHYAZQBzAB0gIABrAHIAKwF6AGUAcwAgAGMAZQBuAHQAcgBhAG0AIABhAHIAIABuAGEAdQBkAGkA RgF1ACwAIABhAHAAIwETAXIAYgB1ACAAdgBhAGkAIABlAGwAZQBrAHQAcgBvAG4AaQBrAHUALgAg AA0ADQBVAE4AIABUAEEAUwAgAFYASQBTAFMAIABCAEUAWgAgAE0AQQBLAFMAQQBTACEADQANAE4A bwBzAGwAEwFnAHUAbQABASAAcABhAHMAAQFrAHUAbQBhACAAYQBwAG0AZQBrAGwAEwF0AAEBagBp ACAAdgBhAHIAEwFzACAAbABhAGkAbQATAXQAIABtAG8AYgBpAGwAbwAgAHQAZQBsAGUAZgBvAG4A dQAgAE4ATwBLAEkAQQAgADYAMwAwADAAIAB1AG4AIABwAG8AcgB0AGEAdAArAXYAbwAgAGQAYQB0 AG8AcgB1ACEADQANAFYAaQBlAG4AAQEgAHQAZQBpAGsAdQBtAAEBIAB2AGEAcgAgAHQAZQBpAGsA dAAsACAAawBhACAAHiBTAHQAdQBkAGUAbgB0AHUAIABQAAABUgBUAEkASgBTACEAIgAgAGkAcgAg AG0AdQB6AGkAawABAWwAaQAgAHUAbgAgAHQAZQBhAHQAcgABAWwAaQAgAGEAdAByAGEAawB0ACsB dgBpACAAYQBrAHQAKwF2AHMAIAB1AG4AIABpAG4AdABlAGwAZQBrAHQAdQABAWwAaQAgAHUAbgAg AGkAbgBmAG8AcgBtAGEAdAArAXYAaQAgAGkAegBnAGwAKwF0AG8AagBvAGEBcwAgAGwAYQBiAGQA YQByACsBYgBhAHMAIABwAGEAcwABAWsAdQBtAHMAIABqAGEAdQBuAGkAZQBhAWkAZQBtAC4ADQAN AFAAYQBzAAEBawB1AG0AAQEgAHAAaQBlAGQAYQBsACsBcwBpAGUAcwAgAGEAcgArASAAYQEBAWQA YQBzACAAbwByAGcAYQBuAGkAegABAWMAaQBqAGEAcwA6ACAAUwB0AHUAZABlAG4AdAB1ACAAawBv AG8AcgBwAG8AcgABAWMAaQBqAHUAIABhAHAAdgBpAGUAbgArAWIAYQAsACAAQgBpAGUAZAByACsB YgBhAHMAIAAiAEwAYQB0AHYAaQBqAGEAcwAgAE0AYQB6AHAAdQBsAGsAaQAiACwAIABMAGEAdAB2 AGkAagBhAHMAIABLAHIAaQBzAHQAKwFnAAEBIABTAHQAdQBkAGUAbgB0AHUAIABiAHIAAQFsACsB YgBhACwAIABBAGcAYQBwAGUAIABMAGEAdAB2AGkAagBhACAAdQAuAGMALgANAA0ATABpAGUAbABz ACAAcABhAGwAZABpAGUAcwAgAG0AawFzAHUAIABhAHQAYgBhAGwAcwB0ACsBdAABAWoAaQBlAG0A OgAgAFIAKwFnAGEAcwAgAEQAbwBtAGUAaQAsACAAQQBDAEMARQBOAFQAVQBSAEUALAAgAFIASQBN AEkALgAgAEsAAQEgAGEAcgArASAAcABhAGwAZABpAGUAcwAgAGQAcgBhAHUAZwBpAGUAbQAuAGwA dgAsACAATABUAFYALAAgAEQASQBFAE4AQQAsACAANQBtAGkAbgAsACAASwBvAG4AdABpACAAQgB1 AHMAcwAsACAAQQBsAGQAYQByAGkAcwAsACAARABvAHUAYgBsAGUAIABDAG8AZgBmAGUAZQAsACAA UAByAG8AVwBlAGIALAAgAFIAKwFnAGEAcwAgAFoAbwBvAGQAAQFyAHoAcwAuAA0ADQBWAGEAaQBy AAEBawAgAGkAbgBmAG8AIAATACAASABZAFAARQBSAEwASQBOAEsAIAAiAGgAdAB0AHAAOgAvAC8A dwB3AHcALgBzAHQAdQBkAGUAbgB0AHAAYQByAHQAeQAuAGwAdgAiACAAFAB3AHcAdwAuAHMAdAB1 AGQAZQBuAHQAcABhAHIAdAB5AC4AbAB2ABUALgANAA0AMQA0AC4AMAA5AC4AMgAwADAANwANAA0A DQANAFMAYQBnAGEAdABhAHYAbwBqAGEADQBTAGEAbgBkAGEAIABCAGwAbwBtAGsAYQBsAG4AYQAN AA0AUgBlAGQAaQAjARMBagBhAA0AQQBnAHIAaQBzACAAQQBtAGUAcgBpAGsAcwANADIANgA0ADYA MQAxADAAMQANAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC0FwAAxBcAAAgYAAAMGAAA fBgAAH4YAACAGAAAHhkAAFgZAADIGQAAyhkAAMwZAAAgGwAAIhsAADwbAAA+GwAAjhsAAJAbAAC2 GwAAuBsAALwbAAC+GwAAwBsAAMIbAADSGwAA1BsAANobAADwGwAADhwAABAcAABSHAAA8ujy6OHa 1c3Vwbq2sq6moqaZpq6itpW2srayjrK2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwVaKUKvAAWaKUKvAAABhZo 6W5LAAAQFWgGBf4AFmjvdpsAMEoQAAAGFmjvdpsAAA8DagAAAAAWaO92mwBVCAEGFmgDHEgAAAYW aLpotwAABhZopQq8AAAMFWhPOIMAFmilCrwAABcVaK938QAWaK938QBdCIFtSAUEc0gFBA8VaK93 8QAWaK938QBdCIEJFmivd/EAXQiBDBZor3fxADUIgV0IgQAMFmilCrwANQiBXQiBABIVaKUKvAAW aKUKvAA1CIFdCIEAGhVopQq8ABZopQq8ADUIgV0IgW1IBQRzSAUEHtQbAADWGwAA2BsAANobAADw GwAAEBwAABIcAAAkHAAAQBwAAFIcAAD9AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAP0AAAAAAAAA AAAAAAD9AAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAD4AAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAZ2SlCrwAAAEAAAAJLAAxkGgBH7CCLiCwxkEhsKUG IrBSAyOQbgQkkG4EJbAAABewxAIYsMQCDJDEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABeBBEAEgABAAsBDwAHAAAAAAAA AAAABAAIAAAAmAAAAJ4AAACeAAAAngAAAJ4AAACeAAAAngAAAJ4AAACeAAAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAAdgIAAHYCAAB2AgAAdgIAAHYCAAB2AgAAdgIAAHYCAAB2 AgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAPgIAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAAA2BgAANgYAAKgAAAA2BgAANgYAABYAAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAALgAAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAABoAQAASAEAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAsAMAADYG AAAyBgAAGAAAAMADAADQAwAA4AMAAPADAAAABAAAEAQAACAEAAAwBAAAQAQAAFAEAABgBAAAcAQA AIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAMgYAACgCAADYAQAA6AEAACAEAAAwBAAA QAQAAFAEAABgBAAAcAQAAIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAIAQAADAEAABA BAAAUAQAAGAEAABwBAAAgAQAAJAEAADAAwAA0AMAAOADAADwAwAAAAQAABAEAAAgBAAAMAQAAEAE AABQBAAAYAQAAHAEAACABAAAkAQAAMADAADQAwAA4AMAAPADAAAABAAAEAQAACAEAAAwBAAAQAQA AFAEAABgBAAAcAQAAIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAIAQAADAEAABABAAA UAQAAGAEAABwBAAAgAQAAJAEAADAAwAA0AMAAOADAADwAwAAAAQAABAEAAAgBAAAMAQAAEAEAABQ BAAAYAQAAHAEAACABAAAkAQAADgBAABYAQAA+AEAAAgCAAAYAgAAVgIAAH4CAAAUAAAAX0gBBG1I CQRuSAkEc0gJBHRICQQAAAAAQAAAYPH/AgBAAAwQAAAAAAAAAAAGAE4AbwByAG0AYQBsAAAAAgAA ABgAQ0oYAF9IAQRhShgAbUgmBHNIJgR0SBkEAAAAAAAAAAAAAAAAAAAAAAAARABBYPL/oQBEAAwB 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 xnzSKn914qN74OgtuN+fMCVNMME3JYGh9RyYPIDktxzN0o2/AAAA//8DAFBLAwQUAAYACAAAACEA DdGQn7YAAAAbAQAAJwAAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwucmVsc4SP TQrCMBSE94J3CG9v07oQkSbdiNCt1AOE5DUNNj8kUeztDa4sCC6HYb6ZabuXnckTYzLeMWiqGgg6 6ZVxmsFtuOyOQFIWTonZO2SwYIKObzftFWeRSyhNJiRSKC4xmHIOJ0qTnNCKVPmArjijj1bkIqOm Qci70Ej3dX2g8ZsBfMUkvWIQe9UAGZZQmv+z/TgaiWcvHxZd/lFBc9mFBSiixszgI5uqTATKW7q6 xN8AAAD//wMAUEsBAi0AFAAGAAgAAAAhAIKKvBP6AAAAHAIAABMAAAAAAAAAAAAAAAAAAAAAAFtD b250ZW50X1R5cGVzXS54bWxQSwECLQAUAAYACAAAACEApdan58AAAAA2AQAACwAAAAAAAAAAAAAA AAArAQAAX3JlbHMvLnJlbHNQSwECLQAUAAYACAAAACEAa3mWFoMAAACKAAAAHAAAAAAAAAAAAAAA AAAUAgAAdGhlbWUvdGhlbWUvdGhlbWVNYW5hZ2VyLnhtbFBLAQItABQABgAIAAAAIQCWta3ilgYA AFAbAAAWAAAAAAAAAAAAAAAAANECAAB0aGVtZS90aGVtZS90aGVtZTEueG1sUEsBAi0AFAAGAAgA AAAhAA3RkJ+2AAAAGwEAACcAAAAAAAAAAAAAAAAAmwkAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1l TWFuYWdlci54bWwucmVsc1BLBQYAAAAABQAFAF0BAACWCgAAAAA8P3htbCB2ZXJzaW9uPSIxLjAi IGVuY29kaW5nPSJVVEYtOCIgc3RhbmRhbG9uZT0ieWVzIj8+DQo8YTpjbHJNYXAgeG1sbnM6YT0i aHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL2RyYXdpbmdtbC8yMDA2L21haW4iIGJn MT0ibHQxIiB0eDE9ImRrMSIgYmcyPSJsdDIiIHR4Mj0iZGsyIiBhY2NlbnQxPSJhY2NlbnQxIiBh Y2NlbnQyPSJhY2NlbnQyIiBhY2NlbnQzPSJhY2NlbnQzIiBhY2NlbnQ0PSJhY2NlbnQ0IiBhY2Nl bnQ1PSJhY2NlbnQ1IiBhY2NlbnQ2PSJhY2NlbnQ2IiBobGluaz0iaGxpbmsiIGZvbEhsaW5rPSJm b2xIbGluayIvPgAAAADqBwAADAAAIgAAAAD/////AAgAALQXAABSHAAACAAAAA8AAAAACAAA1BsA AFIcAAAJAAAAEAAAAF8HAACIBwAAnAcAAOoHAAATWBT/FYAPAADwOAAAAAAABvAYAAAAAggAAAIA AAABAAAAAQAAAAEAAAACAAAAQAAe8RAAAAD//wAAAAD/AICAgAD3AAAQAA8AAvCSAAAAEAAI8AgA AAABAAAAAQQAAA8AA/AwAAAADwAE8CgAAAABAAnwEAAAAAAAAAAAAAAAAAAAAAAAAAACAArwCAAA AAAEAAAFAAAADwAE8EIAAAASAArwCAAAAAEEAAAADgAAUwAL8B4AAAC/AQAAEADLAQAAAAD/AQAA CAAEAwkAAAA/AwEAAQAAABHwBAAAAAEAAAD//wEAAAALADAALgAxAF8AdABhAGIAbABlADEANgDl BAAA7AcAAAAAAADlBAAA7AcAAAAAAAA1AAAAQAAAAEIAAABFAAAASAAAAFAAAABTAAAAawAAAHEA AAB3AAAAfgAAAI8AAACRAAAAqwAAAK0AAACuAAAAsAAAAMwAAADOAAAA2QAAANsAAADmAAAA6AAA AOkAAADrAAAA7QAAAO8AAABRAQAAXQEAAF4BAABhAQAAawEAAGwBAAB1AQAAeAEAAMgBAADOAQAA 3AEAAOQBAAANAgAAEgIAACQCAAAtAgAAnAIAAKACAABqAwAAdAMAAJEDAACZAwAAnAMAAKgDAACw AwAAtwMAAMEDAADGAwAASwQAAE4EAABpBAAAbwQAAJwEAACiBAAArgQAALcEAADiBAAA5gQAAP0E AAAJBQAARQUAAEYFAABPBQAAUwUAAFoFAABiBQAAdwUAAHkFAACKBQAAjwUAAKMFAACkBQAAxwUA AMgFAAD5BQAAAQYAAAgGAAAKBgAADwYAABYGAAAYBgAAGgYAABsGAAAgBgAALQYAAC8GAABOBgAA UAYAAG0GAABvBgAAfwYAAIEGAACOBgAAkwYAAKUGAACtBgAAUQcAAFMHAABXBwAAWgcAAF8HAACg BwAAoQcAAK4HAAC5BwAAvwcAAMgHAADTBwAA6QcAAOwHAAAHAAUABwAFAAcABQAHAAUABwAFAAcA BQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAF AAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUA BwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAH AAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAAAAAAEAAAABEAAAAoAAAA KQAAADIAAAA1AAAAjwAAAJEAAAB2AQAAeAEAACQCAAAmAgAA5AQAAOYEAAD9BAAA/wQAAFoFAABc BQAA/wUAAAEGAAClBgAApwYAAFEHAABTBwAAngcAAKAHAACqBwAArgcAALgHAAC5BwAAyAcAAMoH AADSBwAA0wcAAOAHAADhBwAA6QcAAOwHAAAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAH AAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcAAAAAADMAAACPAAAA kAAAAJAAAACrAAAAzAAAAM0AAADaAAAA2wAAAO8AAABRAQAAdgEAAHcBAAD9BAAABwUAAKYGAACn BgAAnwcAAKAHAACqBwAAqwcAAK4HAAC5BwAA6QcAAOwHAAADAAQAAwAEAAMABAADAAQAAwAEAAMA BAADAAQAAwAEAAMABAADAAQAAwAEAAMABAAHAAAAAAAzAAAAjwAAAJAAAACQAAAAxgAAAMwAAABr AQAAdQEAAHcBAAD9BAAARQUAAFoFAAD/BQAApQYAAF8HAACdBwAAnwcAAJ8HAAChBwAAogcAAK4H AACuBwAAyQcAAMkHAADpBwAA7AcAAAMABAADAAQAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAE AAMABAADAAQAAwAEAAMABwABANkcA1Ue07rY/w//D/8P/w//D/8P/w//D/8PAAABAAAAAAABAAAA AAAAAAAAAAAAAAAAAAAAGAAAD4TQAhGEmP4VxgUAAdACBl6E0AJghJj+AgAAAC4AAQAAAAQAAQAA AAAAAAAAAAAAAAAAAAAAABgAAA+EoAURhJj+FcYFAAGgBQZehKAFYISY/gIAAQAuAAEAAAAAgAEA AAAAAAAAAAAAAAAAAAAAAAAYAAAPhHAIEYSY/hXGBQABcAgGXoRwCGCEmP4CAAIALgABAAAAAIAB AAAAAAAAAAAAAAAAAAAAAAAAGAAAD4RACxGEmP4VxgUAAUALBl6EQAtghJj+AgADAC4AAQAAAACA AQAAAAAAAAAAAAAAAAAAAAAAABgAAA+EEA4RhJj+FcYFAAEQDgZehBAOYISY/gIABAAuAAEAAAAA gAEAAAAAAAAAAAAAAAAAAAAAAAAYAAAPhOAQEYSY/hXGBQAB4BAGXoTgEGCEmP4CAAUALgABAAAA AIABAAAAAAAAAAAAAAAAAAAAAAAAGAAAD4SwExGEmP4VxgUAAbATBl6EsBNghJj+AgAGAC4AAQAA AACAAQAAAAAAAAAAAAAAAAAAAAAAABgAAA+EgBYRhJj+FcYFAAGAFgZehIAWYISY/gIABwAuAAEA AAAAgAEAAAAAAAAAAAAAAAAAAAAAAAAYAAAPhFAZEYSY/hXGBQABUBkGXoRQGWCEmP4CAAgALgAB AAAA2RwDVQAAAAAAAAAAAAAAAP///////wEAAAAAAP//AQAAAAAAAQAAfhdaAAAAAAAAAAAAAQIA AgAVAAAABAAAAAgAAADlAAAAAAAAABQAAAAVKQ4APCAQAAMcSADpbksAwRRQAK4CbgDpYnYA+k97 AE84gwC8G5AAWnSSAO92mwBzfaMAszapAD0stwC6aLcAsBG5AKUKvADQM+oAJmHsAK938QAAAAAA 6gcAAOwHAAAAAAAAAQAAAP9AAwABAAAAAADqBwAAABgTAwEAAQAAAAAAAQAAAAAAAAAAAAAAAhwA AAAAAAAAwQMAAOoHAABgAAAIAAAAAGAAABQAAAAA//8BAAAABwBVAG4AawBuAG8AdwBuAP//AQAI AAAAAAAAAAAAAAD//wEAAAAAAP//AAACAP//AAAAAP//AAACAP//AAAAAAQAAABHHpABAAACAgYD BQQFAgMEhyoAIAAAAIAIAAAAAAAAAP8BAAAAAAAAVABpAG0AZQBzACAATgBlAHcAIABSAG8AbQBh AG4AAAA1EJABAgAFBQECAQcGAgUHAAAAAAAAABAAAAAAAAAAAAAAAIAAAAAAUwB5AG0AYgBvAGwA AAAzLpABAAACCwYEAgICAgIEhyoAIAAAAIAIAAAAAAAAAP8BAAAAAAAAQQByAGkAYQBsAAAAQR6Q AQAAAgQFAwUEBgMCBO8CAKDrIABCAAAAAAAAAACfAAAAAAAAAEMAYQBtAGIAcgBpAGEAIABNAGEA dABoAAAAIgAEAPEIiBgA8MQCAABoAQAAAABUcrmmWHK5pgAAAAADAAgAAAAuAQAAvAYAAAEABAAA AAQAAxAOAAAALgEAALwGAAABAAQAAAAOAAAAAAAAACEDAPAQAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAKUGbgS0ALQAgYFyNAAAEAAZAGQAAAAZAAAA5gcAAOYHAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAADKD UQDwEAAIAP/9AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhIUAAAAAAJ8P8PAQgBPwAA4wQAAP// /3////9/////f////3////9/////f////3+wEbkAAAQAADIAAAAAAAAAAAAAAAAAAAAAAAAAAAAh BAAAAAAAAAAAAAAAAAAAAAAAABAcAAADAAAAAAAAAAAAeAAAAHgAAAAAAAAAAAAAAKAFAAD//xIA AAAAAAAAEABTAHQAdQBkAGUAbgB0AHUAIABQAAABUgBUAEkASgBTAAAAAAAAAA8AUwBhAG4AZABh ACAAQgBsAG8AbQBrAGEAbABuAGEABwBBAG0AZQByAGkAawBzAAAAAAAAAAAAAAAAAAAAAAAAAAAA EAAAAAYAAAABAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v8AAAUBAgAAAAAAAAAAAAAAAAAAAAAAAQAAAOCFn/L5T2gQ q5EIACsns9kwAAAAhAEAABEAAAABAAAAkAAAAAIAAACYAAAAAwAAALQAAAAEAAAAwAAAAAUAAADY AAAABgAAAOQAAAAHAAAA8AAAAAgAAAAEAQAACQAAABQBAAASAAAAIAEAAAoAAABAAQAADAAAAEwB AAANAAAAWAEAAA4AAABkAQAADwAAAGwBAAAQAAAAdAEAABMAAAB8AQAAAgAAAOn9AAAeAAAAFAAA AFN0dWRlbnR1IFDEgFJUSUpTAAAAHgAAAAQAAAAAAAAAHgAAABAAAABTYW5kYSBCbG9ta2FsbmEA HgAAAAQAAAAAAAAAHgAAAAQAAAAAAAAAHgAAAAwAAABOb3JtYWwuZG90bQAeAAAACAAAAEFtZXJp a3MAHgAAAAQAAAAzAAAAHgAAABgAAABNaWNyb3NvZnQgT2ZmaWNlIFdvcmQAAABAAAAAADAaHgEA AABAAAAAAOhLR5f2xwFAAAAAAABZ1pf2xwEDAAAAAQAAAAMAAAAuAQAAAwAAALwGAAADAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAP7/AAAFAQIAAAAAAAAAAAAAAAAAAAAAAAIAAAAC1c3VnC4bEJOXCAArLPmu RAAAAAXVzdWcLhsQk5cIACss+a5AAQAA/AAAAAwAAAABAAAAaAAAAA8AAABwAAAABQAAAIAAAAAG AAAAiAAAABEAAACQAAAAFwAAAJgAAAALAAAAoAAAABAAAACoAAAAEwAAALAAAAAWAAAAuAAAAA0A AADAAAAADAAAAN4AAAACAAAA6f0AAB4AAAAIAAAAbWFqYXMAAAADAAAADgAAAAMAAAAEAAAAAwAA AOYHAAADAAAAAAAMAAsAAAAAAAAACwAAAAAAAAALAAAAAAAAAAsAAAAAAAAAHhAAAAEAAAASAAAA U3R1ZGVudHUgUMSAUlRJSlMADBAAAAIAAAAeAAAABgAAAFRpdGxlAAMAAAABAAAAuAAAAAMAAAAA AAAAIAAAAAEAAAA4AAAAAgAAAEAAAAABAAAAAgAAAAwAAABfUElEX0hMSU5LUwACAAAA6f0AAEEA AABwAAAABgAAAAMAAAA5AHEAAwAAAAAAAAADAAAAAAAAAAMAAAAFAAAAHwAAABwAAABoAHQAdABw ADoALwAvAHcAdwB3AC4AcwB0AHUAZABlAG4AdABwAGEAcgB0AHkALgBsAHYALwAAAB8AAAABAAAA AAB5AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAA DgAAAA8AAAAQAAAAEQAAAP7///8TAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAc AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAA/v///yQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoA AAD+////LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAAP7////9////NQAAAP7////+/////v// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /////1IAbwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAWAAUB//////////8DAAAABgkCAAAAAADAAAAAAAAARgAAAAAAAAAAAAAAAJCy 6emX9scBNwAAAIAAAAAAAAAAMQBUAGEAYgBsAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAgH/////BQAAAP////8AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAFyEAAAAAAABXAG8AcgBkAEQAbwBjAHUAbQBlAG4AdAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGgACAQEAAAD//////////wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuIgAAAAAAAAUAUwB1AG0AbQBh AHIAeQBJAG4AZgBvAHIAbQBhAHQAaQBvAG4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAIB AgAAAAQAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAQAAAA AAAABQBEAG8AYwB1AG0AZQBuAHQAUwB1AG0AbQBhAHIAeQBJAG4AZgBvAHIAbQBhAHQAaQBvAG4A AAAAAAAAAAAAADgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAArAAAAABAAAAAAAAABAEMAbwBtAHAATwBiAGoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgACAP///////////////wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////AAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ //////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAABAAAA/v////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /wEA/v8DCgAA/////wYJAgAAAAAAwAAAAAAAAEYnAAAATWljcm9zb2Z0IE9mZmljZSBXb3JkIDk3 LTIwMDMgRG9jdW1lbnQACgAAAE1TV29yZERvYwAQAAAAV29yZC5Eb2N1bWVudC44APQ5snEAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ------=_Part_979_8743786.1189751181201-- --9B095B5ADSN=_01C7DED0EB082C7600015D0ALVAEI?EXCH.lvaei-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_14.txt.eml000066400000000000000000001256261502127241700246610ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs84361ugm; Wed, 12 Sep 2007 22:26:04 -0700 (PDT) Received: by 10.66.249.8 with SMTP id w8mr2616245ugh.1189661164236; Wed, 12 Sep 2007 22:26:04 -0700 (PDT) Return-Path: <> Received: from regsers.diena.lv (regsers.diena.lv [159.148.131.3]) by mx.google.com with SMTP id m1si170995ugc.2007.09.12.22.25.57; Wed, 12 Sep 2007 22:26:04 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of regsers.diena.lv designates 159.148.131.3 as permitted sender) client-ip=159.148.131.3; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of regsers.diena.lv designates 159.148.131.3 as permitted sender) smtp.mail= Message-Id: <46e8c9e6.0172420a.5581.12b7SMTPIN_ADDED@mx.google.com> Received: (qmail 14150 invoked for bounce); 13 Sep 2007 11:30:06 +0300 Date: 13 Sep 2007 11:30:06 +0300 From: postmaster@regsers.diena.lv To: agris.ameriks@gmail.com Subject: failure notice Hi. This is the qmail-send program at regsers.diena.lv. I'm afraid I wasn't able to deliver your message to the following addresses. This is a permanent error; I've given up. Sorry it didn't work out. : Message rejected. Not enough storage space in user's mailbox to accept message. --- Below this line is a copy of the message. Return-Path: Received: (qmail 14146 invoked by uid 89); 13 Sep 2007 11:30:06 +0300 Delivered-To: ogreszinas.lv-irina.vavere@ogreszinas.lv Received: (qmail 14141 invoked by uid 89); 13 Sep 2007 11:30:06 +0300 Delivered-To: ogreszinas.lv-info@ogreszinas.lv Received: (qmail 14139 invoked from network); 13 Sep 2007 11:30:06 +0300 Received: from unknown (HELO mesers.diena.lv) (159.148.85.166) by regsers.diena.lv with SMTP; 13 Sep 2007 11:30:06 +0300 Received: from hu-out-0506.google.com (hu-out-0506.google.com [72.14.214.238]) by mesers.diena.lv (Postfix) with ESMTP id 64DFB25651 for ; Thu, 13 Sep 2007 08:25:43 +0300 (EEST) Received: by hu-out-0506.google.com with SMTP id 16so146768hue for ; Wed, 12 Sep 2007 22:25:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:cc:mime-version:content-type; bh=L2SyivlqHxtTjH5g4+fQw5fs6kGsOHDdZAIpOVCV8D8=; b=RM2JXrCYFBhkLe4yd6MxT0vmzvsHsyA32ylCdGEs6dezOLBcyT3lgh/t+qJ6XgJpJyDA6z2lEt3GRh8UvPhjD4Kl/Y6IuiNcuzZWVAXZ7MGTBiv+3wSBCU+pyQsQDGkZWLkBTX6MgAthHhOzZAhXnyfM1Oe1Dgg0Q2UCaz9bXY4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:cc:mime-version:content-type; b=FRTW/6SXI8LlxwFqs1q82QJK0pNGrTWJ1KVsKIqnA++p++EGtHGryiAagnHG4s91WGGZ8IeRnQhiEPDqyG3GHqpYZR3GzmsluDdLGsKhi75yYfwXq8p+XAQ9AGGMFwYpq2RCrumuHNWWnAPLFvkIQePM/4aLKJ9Ms/ORwiinMXY= Received: by 10.67.26.7 with SMTP id d7mr2639740ugj.1189661104603; Wed, 12 Sep 2007 22:25:04 -0700 (PDT) Received: by 10.67.102.1 with HTTP; Wed, 12 Sep 2007 22:25:04 -0700 (PDT) Message-ID: Date: Thu, 13 Sep 2007 08:25:04 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?15.septembr=EE_Studentu_P=E2rtijs!?= Cc: "=?ISO-8859-13?Q?J=E2nis_Erts?=" , "Edgars Gutkis" , "Toms Beinerts" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_8802_28973416.1189661104602" X-asdiena.lv-MailScanner: Found to be clean X-asdiena.lv-MailScanner-SpamCheck: not spam, SpamAssassin (score=0, required 5.4) X-asdiena.lv-MailScanner-From: agris.ameriks@gmail.com ------=_Part_8802_28973416.1189661104602 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline TGFiZGllbiEKCjE1LnNlcHRlbWJy7iBFc3BsYW7iZOcgbm90aWtzIFN0dWRlbnR1IFDCUlRJSlMh CgpOb3P7dHUgSnVtcyByZWzuemkuCgotLSAKQXIgY2ll8nUsCkFncmlzIEFtZXJpa3MKTW9iLnRl bC4gMiA2NDYxMTAxCkUtcGFzdHM6IEFncmlzLkFtZXJpa3NAZ21haWwuY29tCg== ------=_Part_8802_28973416.1189661104602 Content-Type: application/msword; name="Studentu_Partijs_relize.doc" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="Studentu_Partijs_relize.doc" X-Attachment-Id: f_f6ith33e 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAABAAAANAAAAAAAAAAA EAAANgAAAAEAAAD+////AAAAADMAAAD///////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////s pcEAW4AJBAAA8BK/AAAAAAAAEAAAAAAACAAAlhwAAA4AYmpiaqz6rPoAAAAAAAAAAAAAAAAAAAAA AAAJBBYALiIAAM6QAQDOkAEAGQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//w8AAAAA AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAALcAAAAAAAAGAAAAAAAAAAYAAEMT AAAAAAAAQxMAAAAAAABDEwAAAAAAAEMTAAAAAAAAQxMAABQAAAAAAAAAAAAAAP////8AAAAAVxMA AAAAAABXEwAAAAAAAFcTAAAAAAAAVxMAABQAAABrEwAAFAAAAFcTAAAAAAAAKRwAAPgAAAB/EwAA FgAAAJUTAAAAAAAAlRMAAAAAAACVEwAAAAAAAJUTAAAAAAAAcBQAAB4AAACOFAAADAAAAJoUAAAI AAAAwBsAAAIAAADCGwAAAAAAAMIbAAAAAAAAwhsAAAAAAADCGwAAAAAAAMIbAAAAAAAAwhsAAAAA AAAhHQAAogIAAMMfAABuAAAAwhsAACEAAAAAAAAAAAAAAAAAAAAAAAAAQxMAAAAAAACiFAAAAAAA AAAAAAAAAAAAAAAAAAAAAABwFAAAAAAAAHAUAAAAAAAAohQAAAAAAACiFAAAAAAAAMIbAAAAAAAA AAAAAAAAAABDEwAAAAAAAEMTAAAAAAAAlRMAAAAAAAAAAAAAAAAAAJUTAADbAAAA4xsAABYAAADk GAAAAAAAAOQYAAAAAAAA5BgAAAAAAACiFAAAngIAAEMTAAAAAAAAlRMAAAAAAABDEwAAAAAAAJUT AAAAAAAAwBsAAAAAAAAAAAAAAAAAAOQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAohQAAAAAAADAGwAAAAAAAAAAAAAAAAAA5BgAAAAAAADkGAAA HgAAAAwbAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANBsAAAAAAACVEwAAAAAAAP////8AAAAA8CKPS8b1 xwEAAAAAAAAAAFcTAAAAAAAAQBcAANYAAAAkGwAACAAAAAAAAAAAAAAArBsAABQAAAD5GwAAMAAA ACkcAAAAAAAALBsAAAgAAAAxIAAAAAAAABYYAACOAAAAMSAAABAAAAA0GwAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0 GwAAFAAAADEgAAAAAAAAAAAAAAAAAABDEwAAAAAAAEgbAABkAAAAohQAAAAAAACiFAAAAAAAAOQY AAAAAAAAohQAAAAAAACiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAohQA AAAAAACiFAAAAAAAAKIUAAAAAAAAwhsAAAAAAADCGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAApBgAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKIUAAAA AAAAohQAAAAAAACiFAAAAAAAACkcAAAAAAAAohQAAAAAAACiFAAAAAAAAKIUAAAAAAAAohQAAAAA AAAAAAAAAAAAAP////8AAAAA/////wAAAAD/////AAAAAAAAAAAAAAAA/////wAAAAD/////AAAA AP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAAP////8AAAAA /////wAAAAD/////AAAAAP////8AAAAA/////wAAAAD/////AAAAADEgAAAAAAAAohQAAAAAAACi FAAAAAAAAKIUAAAAAAAAohQAAAAAAACiFAAAAAAAAKIUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiFAAAAAAAAKIUAAAAAAAAohQA AAAAAAAABgAACQwAAAkSAAA6AQAABQASAQAACQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFMAdAB1 AGQAZQBuAHQAdQAgAFAAAAFSAFQASQBKAFMADQAyADAAMAA3AC4AIABnAGEAZABhACAAMQA1AC4A cwBlAHAAdABlAG0AYgByACsBDQBFAHMAcABsAGEAbgABAWQAEwENAA0AMQA1AC4AIABzAGUAcAB0 AGUAbQBiAHIAKwEgAFIAKwFnAAEBLAAgAEUAcwBwAGwAYQBuAAEBZAATASwAIABKAGEAdQBuAGkA ZQBhAXUAIABtABMBbgBlAGEBYQAgAGkAZQB0AHYAYQByAG8AcwAgAG4AbwB0AGkAZQBrACAAcABh AHMAAQFrAHUAbQBzACAAagBhAHUAbgBpAGUAYQFpAGUAbQAgAC0AIABTAHQAdQBkAGUAbgB0AHUA IABQAAABUgBUAEkASgBTAC4AIAAgAFAAcgBvAGoAZQBrAHQAdQAgAG8AcgBnAGEAbgBpAHoAEwEg AEwAYQB0AHYAaQBqAGEAcwAgAFUAbgBpAHYAZQByAHMAaQB0AAEBdABlAHMAIABGAGkAegBpAGsA YQBzACAAdQBuACAAbQBhAHQAZQBtAAEBdABpAGsAYQBzACAAZgBhAGsAdQBsAHQAAQF0AGUAcwAg AEYAaQB6AG0AYQB0AHUAIABhAHQAYgBhAGwAcwB0AGEAIABmAG8AbgBkAHMAIABzAGEAZABhAHIA YgArAWIAAQEgAGEAcgAgAEwAYQB0AHYAaQBqAGEAcwAgAE0AAQFrAHMAbABhAHMAIABhAGsAYQBk ABMBbQBpAGoAYQBzACwAIABMAGEAdAB2AGkAagBhAHMAIABLAHUAbAB0AGsBcgBhAHMAIABhAGsA YQBkABMBbQBpAGoAYQBzACAAdQBuACAASgABAXoAZQBwAGEAIABWACsBdABvAGwAYQAgAEwAYQB0 AHYAaQBqAGEAcwAgAE0AawF6AGkAawBhAHMAIABhAGsAYQBkABMBbQBpAGoAYQBzACAAUwB0AHUA ZABlAG4AdAB1ACAAcABhAGEBcAABAXIAdgBhAGwAZAATAW0ALgANAA0ATQATAXIANwFpAHMAIAAt ACAAbwByAGcAYQBuAGkAegATAXQAIABwAGEAcwABAWsAdQBtAHUAIABzAHQAdQBkAGUAbgB0AGkA ZQBtACwAIABrAHUAcgAgAGoAYQB1AG4AaQBlAGEBaQAgAHYAYQByABMBcwAgAGQAYQB1AGQAegBw AHUAcwArAWcAaQAgAHAAYQB2AGEAZAArAXQAIABsAGEAaQBrAHUALAAgAHQAdQByAGsAbAABAXQA LAAgAHAAYQByACAAYgByACsBdgB1AC4AIABTAHQAdQBkAGUAbgB0AGkAZQBtACAAdABpAGsAcwAg AHAAaQBlAGQAAQF2AAEBdABzACAAdgATAXIAbwB0ACAAbQB1AHoAaQBrAAEBbAB1AHMAIAB1AG4A IAB0AGUAYQB0AHIAAQFsAHUAcwAgAHAAcgBpAGUAawBhAW4AZQBzAHUAbQB1AHMALAAgAHAAYQBw AGkAbABkAGkAbgABAXQAIABzAGEAdgBhAHMAIAB6AGkAbgABAWEBYQBuAGEAcwAgAGEAcABtAGUA awBsABMBagBvAHQAIABkAGEAfgEBAWQAdQBzACAAcwBlAG0AaQBuAAEBcgB1AHMAIAB1AG4AIABs AGUAawBjAGkAagBhAHMAIABwAGEAcgAgAHoAaQBuAAEBdABuAGkALAAgAHAAaQBlAGQAYQBsACsB dABpAGUAcwAgAGQAYQB+AQEBZAABAXMAIABhAGsAdABpAHYAaQB0AAEBdAATAXMAIAB1AG4AIABz AHAAEwFsABMBcwAgACgAcwB0AGEAZgBlAHQAZQBzACwAIABhAWEAaABhACAAdAB1AHIAbgArAXIA cwAsACAAQQBDAEMARQBOAFQAVQBSAEUAIAByAG8AZABlAG8ALAAgAFIASQBNAEkAIABrAGwAaQBu AGEBawABAXAAEwFqAHUAIABzAGkAZQBuAGEAIAB1AC4AYwAuACkALAAgAGcAawF0ACAAcAByAGEA awB0AGkAcwBrAGEAcwAgAGkAZQBtAGEARgFhAHMAIABzAGEAbABzAGEAcwAgAG4AbwBkAGEAcgBi ACsBYgABAXMALAAgAGsAAQEgAGEAcgArASAAdgATAXIAbwBqAG8AdAAgAGoAYQB1AG4AbwBzACAA bQABAWsAcwBsAGkAbgBpAGUAawB1AHMAIABuAG8AIABMAE0AQQAgAHYAaQBGAXUAIAByAGEAZABv AGEBYQBqAAEBIABkAGEAcgBiAAEBLAAgAGsAdQByACAAdgBhAHIAEwFzACAAaQBlAHMAYQBpAHMA dAArAXQAaQBlAHMAIABhAHIAKwEgAHMAawBhAHQAKwF0AAEBagBpAC4AIABTAHQAdQBkAGUAbgB0 AGkAZQBtACAAYgBrAXMAIABpAHoAZABlAHYAKwFiAGEAIABpAGUAcABhAHoAKwF0AGkAZQBzACAA dQBuACAAaQBlAHMAYQBpAHMAdAArAXQAaQBlAHMAIABvAHIAZwBhAG4AaQB6AAEBYwBpAGoAAQFz AC4AIABWAGkAcwBhACAAcABhAHMAAQFrAHUAbQBhACAAbABhAGkAawABASAAYQBwAG0AZQBrAGwA EwF0AAEBagBpAGUAbQAgAGIAawFzACAAaQBlAHMAcAATAWoAYQAgAHAAYQBsACsBZAB6ABMBdAAg ACIATQBhAHIAcwBhACAAZwBhAHQAdgBlAHMAHSAgAGsAcgArAXoAZQBzACAAYwBlAG4AdAByAGEA bQAgAGEAcgAgAG4AYQB1AGQAaQBGAXUALAAgAGEAcAAjARMBcgBiAHUAIAB2AGEAaQAgAGUAbABl AGsAdAByAG8AbgBpAGsAdQAuACAADQANAFUAegBzAHQAAQFzAGkAZQBzACAAcABvAHAAdQBsAAEB cgBhAHMAIABnAHIAdQBwAGEAcwAsACAAawABASAAQwBSAEUARABPACwAIABNAG8AcgBuAGkAbgBn ACAAQQBmAHQAZQByACwAIABWAG8AaQBjAGUAawBzACAAVgBvAGkAcwBrAGEALAAgAFMAbwB1AG4A ZABBAHIAYwBhAGQAZQAsACAAQwBhAGMAbwBwAGgAbwBuAGkAYwBzACwAIABrAAEBIABhAHIAKwEg AGkAbQBwAHIAbwB2AGkAegABAWMAaQBqAGEAcwAgAHQAZQABAXQAcgBpAHMAIABIAGEAbQBsAGUA dABzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAIAgAACII AABoCAAADAkAAA4JAAASCQAAMgkAADYJAABcCQAAEAsAABILAAAUCwAAnA0AAOoNAABeDwAAZA8A AJoPAAAMEAAAJBAAACYQAACMEAAAkBAAAJIQAADIEAAA0hAAABoRAAAcEQAANhEAADgRAABQEQAA UhEAAGARAACOEQAAnBEAAAAWAACIFgAAvBYAANAWAADSFgAA1BYAANYWAAB2FwAAnhcAAKIXAAAu GAAAMBgAADIYAABIGAAAWhgAAMIYAADEGAAAxhgAAPz4/PTw9Ojj9N/839vX29Pby8PLw8vf29O8 tby1vNPw27Wz2/Cv8NurppymnJSmjdONifAAAAAAAAAAAAAABhZoumi3AAAMFWhPOIMAFmhPOIMA AA8VaE84gwAWaE84gwBcCIESFWhPOIMAFmhPOIMANQiBXAiBAAkWaE84gwBcCIEGFmjBFFAAAAYW aDwgEAAAA1UIAQwVaKUKvAAWaK4CbgAADBVopQq8ABZoTziDAAAPFWilCrwAFmhPOIMANQiBDxVo pQq8ABZorgJuADUIgQYWaE84gwAABhZoFSkOAAAGFmiuAm4AAAYWaD0stwAACRZorgJuADUIgQ8V aOlidgAWaOlidgA1CIEGFmilCrwAAAYWaOlidgAABhZo+k97AAAGFmizNqkANAAIAAAiCAAAUggA AGYIAABoCAAAEgsAABQLAACSEAAAlBAAANQWAADWFgAAMBgAADIYAAA0GAAAxBgAAMYYAAAOGgAA EBoAAGYbAABoGwAAABwAAAIcAAAYHAAAGhwAABwcAAAeHAAANBwAAFQcAAD9AAAAAAAAAAAAAAAA /QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA9QAAAAAAAAAAAAAAAP0AAAAA AAAAAAAAAAD1AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAPUAAAAAAAAAAAAAAAD9AAAAAAAAAAAA AAAA9QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA9QAAAAAAAAAAAAAAAP0A AAAAAAAAAAAAAAD1AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAAD9AAAAAAAA AAAAAAAA/QAAAAAAAAAAAAAAAP0AAAAAAAAAAAAAAADtAAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAA AP0AAAAAAAAAAAAAAAD9AAAAAAAAAAAAAAAA/QAAAAAAAAAAAAAAAOgAAAAAAAAAAAAAAAAAAAQA AGdkpQq8AAAHAAADJAJhJAJnZKUKvAAABwAAAyQDYSQDZ2SlCrwAAAEAAAAbIAB1AG4AIABiAGsB cwAgAGkAZQBzAHAAEwFqAGEAIAB2ABMBcgBvAHQALAAgAGsAAQEgAGQAegBpAGUAcwBtAHUAIABk AHUAZQBsACsBIABzAGEAYwBlAG4AYQFhAHMAIAByAGUAawB0AG8AcgBpACAAdQBuACAAcwB0AHUA ZABlAG4AdABpAC4AIABVAHoAcwB0AAEBcwBpAGUAcwAgAGEAcgArASAAdgBpAGoAbwBsAG4AaQBl AGMAZQAgAEMAcgBpAHMAQwBhAG4AdABvAC4ADQANAFAAYQByAGsAAQEgAG4AbwB0AGkAawBzACAA cwBlAG0AaQBuAAEBcgBzACwAIABrAHUAcgBhACAAaQBlAHQAdgBhAHIAbwBzACAAdgBhAHIAEwFz ACAAbgBvAGsAbABhAHUAcwArAXQAaQBlAHMAIABnAHUAZAByAHUAIABjAGkAbAB2ABMBawB1ACAA bABlAGsAYwBpAGoAYQBzADoAIABwAHIAbwBmAC4AIABBAG4AZAByAGUAagBzACAAQwATAWIAZQBy AHMALAAgAHAAcgBvAGYALgAgAEkAdgBhAHIAcwAgAEwAAQFjAGkAcwAsACAASgB1AHIAaQBzACAA YAF0AGUAaQBuAGIAZQByAGcAcwAsACAATwBsAGcAYQAgAFYAKwF0AG8AbABpAEYBYQAsACAASQBs AGcAbwBuAGkAcwAgAFYAaQBsAGsAcwAgAHUALgBjAC4ADQANAA0ATgBvAHMAbAATAWcAdQBtAAEB IABwAGEAcwABAWsAdQBtAGEAIABhAHAAbQBlAGsAbAATAXQAAQFqAGkAIAB2AGEAcgATAXMAIABs AGEAaQBtABMBdAAgAG0AbwBiAGkAbABvACAAdABlAGwAZQBmAG8AbgB1ACAATgBPAEsASQBBACAA NgAzADAAMAAhAA0ADQBWAGkAZQBuAAEBIAB0AGUAaQBrAHUAbQABASAAdgBhAHIAIAB0AGUAaQBr AHQALAAgAGsAYQAgAB4gUwB0AHUAZABlAG4AdAB1ACAAUAAAAVIAVABJAEoAUwAhACIAIABpAHIA IABtAHUAegBpAGsAAQFsAGkAIAB1AG4AIAB0AGUAYQB0AHIAAQFsAGkAIABhAHQAcgBhAGsAdAAr AXYAaQAgAGEAawB0ACsBdgBzACAAdQBuACAAaQBuAHQAZQBsAGUAawB0AHUAAQFsAGkAIAB1AG4A IABpAG4AZgBvAHIAbQBhAHQAKwF2AGkAIABpAHoAZwBsACsBdABvAGoAbwBhAXMAIABsAGEAYgBk AGEAcgArAWIAYQBzACAAcABhAHMAAQFrAHUAbQBzACAAagBhAHUAbgBpAGUAYQFpAGUAbQAuAA0A DQBMAGkAZQBsAHMAIABwAGEAbABkAGkAZQBzACAAbQBrAXMAdQAgAGEAdABiAGEAbABzAHQAKwF0 AAEBagBpAGUAbQA6ACAAUgArAWcAYQBzACAARABvAG0AZQBpACwAIABBAEMAQwBFAE4AVABVAFIA RQAsACAAUgBJAE0ASQAuACAASwABASAAYQByACsBIABwAGEAbABkAGkAZQBzACAAZAByAGEAdQBn AGkAZQBtAC4AbAB2ACwAIABMAFQAVgAsACAARABJAEUATgBBACwAIAA1AG0AaQBuACwAIABLAG8A bgB0AGkAIABCAHUAcwBzACwAIABBAGwAZABhAHIAaQBzACwAIABEAG8AdQBiAGwAZQAgAEMAbwBm AGYAZQBlACwAIABQAHIAbwBXAGUAYgAsACAAUgArAWcAYQBzACAAWgBvAG8AZAABAXIAegBzAC4A DQANAFYAYQBpAHIAAQFrACAAaQBuAGYAbwAgABMAIABIAFkAUABFAFIATABJAE4ASwAgACIAaAB0 AHQAcAA6AC8ALwB3AHcAdwAuAHMAdAB1AGQAZQBuAHQAcABhAHIAdAB5AC4AbAB2ACIAIAAUAHcA dwB3AC4AcwB0AHUAZABlAG4AdABwAGEAcgB0AHkALgBsAHYAFQAuAA0ADQAxADMALgAwADkALgAy ADAAMAA3AA0ADQANAA0AUwBhAGcAYQB0AGEAdgBvAGoAYQANAFMAYQBuAGQAYQAgAEIAbABvAG0A awBhAGwAbgBhAA0ADQBSAGUAZABpACMBEwFqAGEADQBBAGcAcgBpAHMAIABBAG0AZQByAGkAawBz AA0AMgA2ADQANgAxADEAMAAxAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADGGAAA6hgAAPwYAAD+GAAA RBkAAFQZAACYGQAAnBkAAAwaAAAOGgAAEBoAAGQbAABmGwAAgBsAAIIbAADSGwAA1BsAAPobAAD8 GwAAABwAAAIcAAAWHAAAGBwAAB4cAAA0HAAAUhwAAFQcAACWHAAA+vLr4dPh0+HTzMjEwLi0uKu4 wLTIxMjEpMTIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADBVopQq8ABZopQq8AAAQFWgGBf4AFmjvdpsAMEoQ AAAGFmjvdpsAAA8DagAAAAAWaO92mwBVCAEGFmgDHEgAAAYWaLpotwAABhZopQq8AAAMFWhPOIMA FmilCrwAABoVaKUKvAAWaKUKvAA1CIFdCIFtSAUEc0gFBAASFWilCrwAFmilCrwANQiBXQiBAAwW aKUKvAA1CIFdCIEADxVopQq8ABZopQq8AF0IgQkWaKUKvABdCIEAG1QcAABWHAAAaBwAAIQcAACW HAAA+gAAAAAAAAAAAAAAAPoAAAAAAAAAAAAAAAD6AAAAAAAAAAAAAAAA+gAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAGdkpQq8AAAELAAxkGgBH7CCLiCwxkEhsKUG IrBSAyOQbgQkkG4EJbAAABewxAIYsMQCDJDEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABeBBEAEgABAAsBDwAHAAAAAAAA AAAABAAIAAAAmAAAAJ4AAACeAAAAngAAAJ4AAACeAAAAngAAAJ4AAACeAAAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAAdgIAAHYCAAB2AgAAdgIAAHYCAAB2AgAAdgIAAHYCAAB2 AgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAPgIAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAAA2BgAANgYAAKgAAAA2BgAANgYAABYAAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAALgAAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAABoAQAASAEAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYG AAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYA ADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAA NgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2 BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAANgYAADYGAAA2BgAAsAMAADYG AAAyBgAAGAAAAMADAADQAwAA4AMAAPADAAAABAAAEAQAACAEAAAwBAAAQAQAAFAEAABgBAAAcAQA AIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAMgYAACgCAADYAQAA6AEAACAEAAAwBAAA QAQAAFAEAABgBAAAcAQAAIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAIAQAADAEAABA BAAAUAQAAGAEAABwBAAAgAQAAJAEAADAAwAA0AMAAOADAADwAwAAAAQAABAEAAAgBAAAMAQAAEAE AABQBAAAYAQAAHAEAACABAAAkAQAAMADAADQAwAA4AMAAPADAAAABAAAEAQAACAEAAAwBAAAQAQA AFAEAABgBAAAcAQAAIAEAACQBAAAwAMAANADAADgAwAA8AMAAAAEAAAQBAAAIAQAADAEAABABAAA UAQAAGAEAABwBAAAgAQAAJAEAADAAwAA0AMAAOADAADwAwAAAAQAABAEAAAgBAAAMAQAAEAEAABQ BAAAYAQAAHAEAACABAAAkAQAADgBAABYAQAA+AEAAAgCAAAYAgAAVgIAAH4CAAAUAAAAX0gBBG1I CQRuSAkEc0gJBHRICQQAAAAAQAAAYPH/AgBAAAwQAAAAAAAAAAAGAE4AbwByAG0AYQBsAAAAAgAA ABgAQ0oYAF9IAQRhShgAbUgmBHNIJgR0SBkEAAAAAAAAAAAAAAAAAAAAAAAARABBYPL/oQBEAAwB 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 xnzSKn914qN74OgtuN+fMCVNMME3JYGh9RyYPIDktxzN0o2/AAAA//8DAFBLAwQUAAYACAAAACEA DdGQn7YAAAAbAQAAJwAAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1lTWFuYWdlci54bWwucmVsc4SP TQrCMBSE94J3CG9v07oQkSbdiNCt1AOE5DUNNj8kUeztDa4sCC6HYb6ZabuXnckTYzLeMWiqGgg6 6ZVxmsFtuOyOQFIWTonZO2SwYIKObzftFWeRSyhNJiRSKC4xmHIOJ0qTnNCKVPmArjijj1bkIqOm Qci70Ej3dX2g8ZsBfMUkvWIQe9UAGZZQmv+z/TgaiWcvHxZd/lFBc9mFBSiixszgI5uqTATKW7q6 xN8AAAD//wMAUEsBAi0AFAAGAAgAAAAhAIKKvBP6AAAAHAIAABMAAAAAAAAAAAAAAAAAAAAAAFtD b250ZW50X1R5cGVzXS54bWxQSwECLQAUAAYACAAAACEApdan58AAAAA2AQAACwAAAAAAAAAAAAAA AAArAQAAX3JlbHMvLnJlbHNQSwECLQAUAAYACAAAACEAa3mWFoMAAACKAAAAHAAAAAAAAAAAAAAA AAAUAgAAdGhlbWUvdGhlbWUvdGhlbWVNYW5hZ2VyLnhtbFBLAQItABQABgAIAAAAIQCWta3ilgYA AFAbAAAWAAAAAAAAAAAAAAAAANECAAB0aGVtZS90aGVtZS90aGVtZTEueG1sUEsBAi0AFAAGAAgA AAAhAA3RkJ+2AAAAGwEAACcAAAAAAAAAAAAAAAAAmwkAAHRoZW1lL3RoZW1lL19yZWxzL3RoZW1l TWFuYWdlci54bWwucmVsc1BLBQYAAAAABQAFAF0BAACWCgAAAAA8P3htbCB2ZXJzaW9uPSIxLjAi IGVuY29kaW5nPSJVVEYtOCIgc3RhbmRhbG9uZT0ieWVzIj8+DQo8YTpjbHJNYXAgeG1sbnM6YT0i aHR0cDovL3NjaGVtYXMub3BlbnhtbGZvcm1hdHMub3JnL2RyYXdpbmdtbC8yMDA2L21haW4iIGJn MT0ibHQxIiB0eDE9ImRrMSIgYmcyPSJsdDIiIHR4Mj0iZGsyIiBhY2NlbnQxPSJhY2NlbnQxIiBh Y2NlbnQyPSJhY2NlbnQyIiBhY2NlbnQzPSJhY2NlbnQzIiBhY2NlbnQ0PSJhY2NlbnQ0IiBhY2Nl bnQ1PSJhY2NlbnQ1IiBhY2NlbnQ2PSJhY2NlbnQ2IiBobGluaz0iaGxpbmsiIGZvbEhsaW5rPSJm b2xIbGluayIvPgAAAAAZCAAADAAAIgAAAAD/////AAgAAMYYAACWHAAACQAAAA8AAAAACAAAVBwA AJYcAAAKAAAAEAAAAI4HAAC3BwAAywcAABkIAAATWBT/FYAPAADwOAAAAAAABvAYAAAAAggAAAIA AAABAAAAAQAAAAEAAAACAAAAQAAe8RAAAAD//wAAAAD/AICAgAD3AAAQAA8AAvCSAAAAEAAI8AgA AAABAAAAAQQAAA8AA/AwAAAADwAE8CgAAAABAAnwEAAAAAAAAAAAAAAAAAAAAAAAAAACAArwCAAA AAAEAAAFAAAADwAE8EIAAAASAArwCAAAAAEEAAAADgAAUwAL8B4AAAC/AQAAEADLAQAAAAD/AQAA CAAEAwkAAAA/AwEAAQAAABHwBAAAAAEAAAD//wEAAAALADAALgAxAF8AdABhAGIAbABlADEANgA5 BQAAGwgAAAAAAAA5BQAAGwgAAAAAAAB8AAAAhgAAAIcAAACJAAAAkgAAAJkAAADnAAAAAQEAAM4C AADdAgAA5AIAAPUCAAAUAwAAJQMAAK8DAACwAwAAsgMAAMYDAADOAwAABQQAAAYEAAAHBAAADAQA ABIEAAAUBAAAFgQAABsEAAArBAAALwQAAEYEAABUBAAAZAQAAGYEAABnBAAAaQQAAI0EAACPBAAA mgQAAJwEAACnBAAAqQQAAKoEAACsBAAArgQAALAEAAASBQAAHgUAAB8FAAAiBQAALAUAAC0FAAA2 BQAAOQUAAJ0FAACfBQAAsAUAALIFAADCBQAAxAUAAM0FAADTBQAA5QUAAOgFAADwBQAA8gUAAPUF AAD7BQAALwYAADEGAAA1BgAANwYAAD0GAAA/BgAATAYAAE4GAABfBgAAZAYAAHgGAAB5BgAAnAYA AJ0GAADUBgAA1gYAAOUGAADpBgAA9wYAAP8GAAAEBwAABQcAABUHAAAaBwAAPgcAAD8HAABEBwAA RQcAAHIHAAB3BwAAgAcAAIIHAACGBwAAiQcAAI4HAADPBwAA2QcAAN0HAADoBwAA7gcAAPcHAAD5 BwAAGAgAABsIAAAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcA BQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAE AAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUA BwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAH AAUABwAAAAAAEAAAABEAAAAoAAAAKQAAADIAAAA0AAAAiAEAAIoBAABIBAAASgQAABQFAAA3BQAA OQUAAOUFAADoBQAALwYAADEGAADUBgAA1gYAAIAHAACCBwAAzQcAAM8HAADZBwAA3QcAAOcHAADo BwAA9wcAAPkHAAABCAAAAggAAA8IAAAQCAAAGAgAABsIAAAFAAcABQAHAAUABwAFAAcABQAHAAUA BAAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcABQAHAAUABwAFAAcAAAAAAIYAAACH AAAAzgIAAPUCAABkBAAAjQQAAI4EAACbBAAAnAQAALAEAAASBQAANwUAAPAFAADVBgAA1gYAAM4H AADPBwAA2QcAANoHAADdBwAA6AcAABgIAAAbCAAAAwAEAAMABAADAAQAAwAEAAMABAADAAQAAwAE AAMABAADAAQAAwAEAAMABAAHAAAAAADOAgAA9QIAACwFAAA2BQAAjgcAAMwHAADOBwAAzgcAABgI AAAbCAAAAwAEAAMABAADAAQAAwAEAAMABwABANkcA1Ue07rY/w//D/8P/w//D/8P/w//D/8PAAAB AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGAAAD4TQAhGEmP4VxgUAAdACBl6E0AJghJj+AgAAAC4A AQAAAAQAAQAAAAAAAAAAAAAAAAAAAAAAABgAAA+EoAURhJj+FcYFAAGgBQZehKAFYISY/gIAAQAu AAEAAAAAgAEAAAAAAAAAAAAAAAAAAAAAAAAYAAAPhHAIEYSY/hXGBQABcAgGXoRwCGCEmP4CAAIA LgABAAAAAIABAAAAAAAAAAAAAAAAAAAAAAAAGAAAD4RACxGEmP4VxgUAAUALBl6EQAtghJj+AgAD AC4AAQAAAACAAQAAAAAAAAAAAAAAAAAAAAAAABgAAA+EEA4RhJj+FcYFAAEQDgZehBAOYISY/gIA BAAuAAEAAAAAgAEAAAAAAAAAAAAAAAAAAAAAAAAYAAAPhOAQEYSY/hXGBQAB4BAGXoTgEGCEmP4C AAUALgABAAAAAIABAAAAAAAAAAAAAAAAAAAAAAAAGAAAD4SwExGEmP4VxgUAAbATBl6EsBNghJj+ AgAGAC4AAQAAAACAAQAAAAAAAAAAAAAAAAAAAAAAABgAAA+EgBYRhJj+FcYFAAGAFgZehIAWYISY /gIABwAuAAEAAAAAgAEAAAAAAAAAAAAAAAAAAAAAAAAYAAAPhFAZEYSY/hXGBQABUBkGXoRQGWCE mP4CAAgALgABAAAA2RwDVQAAAAAAAAAAAAAAAP///////wEAAAAAAP//AQAAAAAAAQAAfhdaAAAA AAAAAAAAAQIAAgATAAAABAAAAAgAAADlAAAAAAAAABIAAAAVKQ4APCAQAAMcSADBFFAArgJuAOli dgD6T3sATziDALwbkABadJIA73abAHN9owCzNqkAPSy3ALpotwCwEbkApQq8ANAz6gAmYewAAAAA ABkIAAAbCAAAAAAAAAEAAAD/QAIcAAAAAAAAAM4EAAAZCAAAYAAACAAAAABgAAAWAAAAAP//AQAA AAcAVQBuAGsAbgBvAHcAbgD//wEACAAAAAAAAAAAAAAA//8BAAAAAAD//wAAAgD//wAAAAD//wAA AgD//wAAAAAEAAAARx6QAboAAgIGAwUEBQIDBIcqACAAAACACAAAAAAAAAD/AQAAAAAAAFQAaQBt AGUAcwAgAE4AZQB3ACAAUgBvAG0AYQBuAAAANR6QAQIABQUBAgEHBgIFBwAAAAAAAAAQAAAAAAAA AAAAAACAAAAAAFMAeQBtAGIAbwBsAAAAMy6QAboAAgsGBAICAgICBIcqACAAAACACAAAAAAAAAD/ AQAAAAAAAEEAcgBpAGEAbAAAAEEekAG6AAIEBQMFBAYDAgTvAgCg6yAAQgAAAAAAAAAAnwAAAAAA AABDAGEAbQBiAHIAaQBhACAATQBhAHQAaAAAACIABADxCIgYAPDEAgAAaAEAAAAAA2q5hhhquYYA AAAABgAIAAAANQEAAOQGAAABAAQAAAAEAAMQDgAAADUBAADkBgAAAQAEAAAADgAAAAAAAAAhAwDw EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClBm4EtAC0AIGBcjQAABAAGQBkAAAAGQAAABUI AAAVCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAgAAAAAAAAAAAAAyg1EA8BAACAD//QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI SFAAAAAACfD/DwEIAT8AAOMEAAD///9/////f////3////9/////f////3////9/sBG5AAAEAAAy AAAAAAAAAAAAAAAAAAAAAAAAAAAAIQQAAAAAAAAAAAAAAAAAAAAAAAAQHAAAAwAAAAAAAAAAAHgA AAB4AAAAAAAAAAAAAACgBQAA//8SAAAAAAAAABAAUwB0AHUAZABlAG4AdAB1ACAAUAAAAVIAVABJ AEoAUwAAAAAAAAAPAFMAYQBuAGQAYQAgAEIAbABvAG0AawBhAGwAbgBhAAMAQQBBAEEAAAAAAAAA AAAAAAAAAAAAAAAAAAAQAAAABgAAAAEAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAA/v8AAAUBAgAAAAAAAAAAAAAAAAAAAAAAAQAAAOCFn/L5T2gQ q5EIACsns9kwAAAAfAEAABEAAAABAAAAkAAAAAIAAACYAAAAAwAAALQAAAAEAAAAwAAAAAUAAADY AAAABgAAAOQAAAAHAAAA8AAAAAgAAAAAAQAACQAAAAwBAAASAAAAGAEAAAoAAAA4AQAADAAAAEQB AAANAAAAUAEAAA4AAABcAQAADwAAAGQBAAAQAAAAbAEAABMAAAB0AQAAAgAAAOkEAAAeAAAAFAAA AFN0dWRlbnR1IFDCUlRJSlMAAAAAHgAAAAQAAAAAAAAAHgAAABAAAABTYW5kYSBCbG9ta2FsbmEA HgAAAAQAAAAAAAAAHgAAAAQAAAAAAAAAHgAAAAgAAABOb3JtYWwAAB4AAAAEAAAAQUFBAB4AAAAE AAAANgAAAB4AAAAYAAAATWljcm9zb2Z0IE9mZmljZSBXb3JkAAAAQAAAAAAwGh4BAAAAQAAAAAAa JlvD9ccBQAAAAADYKkrG9ccBAwAAAAEAAAADAAAANQEAAAMAAADkBgAAAwAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAP7/AAAFAQIAAAAAAAAAAAAAAAAAAAAAAAIAAAAC1c3VnC4bEJOXCAArLPmu RAAAAAXVzdWcLhsQk5cIACss+a5AAQAA/AAAAAwAAAABAAAAaAAAAA8AAABwAAAABQAAAIAAAAAG AAAAiAAAABEAAACQAAAAFwAAAJgAAAALAAAAoAAAABAAAACoAAAAEwAAALAAAAAWAAAAuAAAAA0A AADAAAAADAAAAN0AAAACAAAA6QQAAB4AAAAIAAAAbWFqYXMAAAADAAAADgAAAAMAAAAEAAAAAwAA ABUIAAADAAAAAAAMAAsAAAAAAAAACwAAAAAAAAALAAAAAAAAAAsAAAAAAAAAHhAAAAEAAAARAAAA U3R1ZGVudHUgUMJSVElKUwAMEAAAAgAAAB4AAAAGAAAAVGl0bGUAAwAAAAEAAAAAuAAAAAMAAAAA AAAAIAAAAAEAAAA4AAAAAgAAAEAAAAABAAAAAgAAAAwAAABfUElEX0hMSU5LUwACAAAA6QQAAEEA AABwAAAABgAAAAMAAAA5AHEAAwAAAAAAAAADAAAAAAAAAAMAAAAFAAAAHwAAABwAAABoAHQAdABw ADoALwAvAHcAdwB3AC4AcwB0AHUAZABlAG4AdABwAGEAcgB0AHkALgBsAHYALwAAAB8AAAABAAAA AAASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAABAAAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAA DgAAAA8AAAAQAAAAEQAAAP7///8TAAAAFAAAABUAAAAWAAAAFwAAABgAAAAZAAAAGgAAABsAAAAc AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAA/v///yQAAAAlAAAAJgAAACcAAAAoAAAAKQAAACoA AAD+////LAAAAC0AAAAuAAAALwAAADAAAAAxAAAAMgAAAP7////9////NQAAAP7////+/////v// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /////1IAbwBvAHQAIABFAG4AdAByAHkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAWAAUB//////////8DAAAABgkCAAAAAADAAAAAAAAARgAAAAAAAAAAAAAAANDr n0vG9ccBNwAAAIAAAAAAAAAAMQBUAGEAYgBsAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAgH/////BQAAAP////8AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAASAAAAQSAAAAAAAABXAG8AcgBkAEQAbwBjAHUAbQBlAG4AdAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGgACAQEAAAD//////////wAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuIgAAAAAAAAUAUwB1AG0AbQBh AHIAeQBJAG4AZgBvAHIAbQBhAHQAaQBvAG4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAIB AgAAAAQAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIwAAAAAQAAAA AAAABQBEAG8AYwB1AG0AZQBuAHQAUwB1AG0AbQBhAHIAeQBJAG4AZgBvAHIAbQBhAHQAaQBvAG4A AAAAAAAAAAAAADgAAgH///////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAArAAAAABAAAAAAAAABAEMAbwBtAHAATwBiAGoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEgACAP///////////////wAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////////AAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/ //////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAABAAAA/v////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /wEA/v8DCgAA/////wYJAgAAAAAAwAAAAAAAAEYnAAAATWljcm9zb2Z0IE9mZmljZSBXb3JkIDk3 LTIwMDMgRG9jdW1lbnQACgAAAE1TV29yZERvYwAQAAAAV29yZC5Eb2N1bWVudC44APQ5snEAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA ------=_Part_8802_28973416.1189661104602-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_15.txt.eml000066400000000000000000000145221502127241700246520ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.66.241.15 with SMTP id o15cs57761ugh; Wed, 29 Aug 2007 06:08:21 -0700 (PDT) Received: by 10.78.177.3 with SMTP id z3mr345635hue.1188392900264; Wed, 29 Aug 2007 06:08:20 -0700 (PDT) Return-Path: <> Received: from mail1.rimibaltic.com (mail1.rimibaltic.com [82.135.221.93]) by mx.google.com with ESMTP id 35si5978420huc.2007.08.29.06.08.13; Wed, 29 Aug 2007 06:08:20 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of mail1.rimibaltic.com designates 82.135.221.93 as permitted sender) client-ip=82.135.221.93; Authentication-Results: mx.google.com; spf=pass smtp.mail=; dkim=hardfail (test mode) header.i=@gmail.com Received: from LT-RELAY (LT-RELAY [127.0.0.1]) by LV-RELAY (Postfix) with SMTP id BB603418F for ; Wed, 29 Aug 2007 16:08:03 +0300 (EEST) Received: from rimilt01.rimi.lan (unknown [10.18.0.224]) by mail1.rimibaltic.com (Postfix) with ESMTP id 58EE0418D for ; Wed, 29 Aug 2007 16:08:01 +0300 (EEST) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type; b=CZ9QIO+5UC0ZclbYqmqs89IO2AWxqJeghMU783zhJTGdMEDpiIbj6737zmC4iP7ZEj+j6hLtvYrBIA+oErYDTTzYht/2B2JHLzalRxWjtqekJD/tLV4R5mUVEg9Y8H/aZSj2YKo2h+EWE1NMHyzZgCN47wKiDEFC/g+YnG93K6k= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type; b=N2pzQqRtteBggnskinEjcQuPTbTCoymrnmzqX7y0xuc4pQUDSd+/89buaDxOK+qbTr7YT+4hgOCzCTwkk9Tr5AXUD+6MMvlDHANDZ3UkGmrwhdAkf/67SGVhCT64aIIVXJ3BcLR71HRLabzkNgNyCp5+Zh/nkMNMnAmqwWIEuXo= Date: Wed, 29 Aug 2007 16:07:26 +0300 From: Postmaster@rimibaltic.com To: "Agris Ameriks" Subject: DELIVERY FAILURE: Recipient user name info (info@rimibaltic.com) not unique. Several matches found in Domino Directory. MIME-Version: 1.0 X-MIMETrack: Itemize by SMTP Server on RIMILT01/RIMILT/RIMI(Release 6.5.3|September 14, 2004) at 2007.08.29 16:09:02, Serialize by Router on RIMILT01/RIMILT/RIMI(Release 6.5.3|September 14, 2004) at 2007.08.29 16:09:03, Serialize complete at 2007.08.29 16:09:03 Message-ID: Content-Type: multipart/report; report-type=delivery-status; boundary="==IFJRGLKFGIR29037UHRUHIHD" --==IFJRGLKFGIR29037UHRUHIHD Content-Type: text/plain; charset=windows-1257 Content-Transfer-Encoding: quoted-printable Your message Subject: Atbalsta l=FBgums was not delivered to: info@rimibaltic.com because: Recipient user name info (info@rimibaltic.com) not unique. Several match= es found in Domino Directory. --==IFJRGLKFGIR29037UHRUHIHD Content-Type: message/delivery-status Reporting-MTA: dns;rimilt01.rimi.lan Final-Recipient: rfc822;info@rimibaltic.com Action: failed Status: 5.0.0 Diagnostic-Code: X-Notes; Recipient user name info (info@rimibaltic.com ) not unique. Several matches found in Domino Directory. --==IFJRGLKFGIR29037UHRUHIHD Content-Type: message/rfc822 Received: from mail1.rimibaltic.com ([172.16.250.54]) by rimilt01.rimi.lan (Lotus Domino Release 6.5.3) with SMTP id 2007082916090093-229401 ; Wed, 29 Aug 2007 16:09:00 +0300 Received: from LT-RELAY (LT-RELAY [127.0.0.1]) by LV-RELAY (Postfix) with SMTP id 11DAF418F for ; Wed, 29 Aug 2007 16:07:54 +0300 (EEST) Received: from nf-out-0910.google.com (nf-out-0910.google.com [64.233.182.186]) by mail1.rimibaltic.com (Postfix) with SMTP id 375504198 for ; Wed, 29 Aug 2007 16:07:46 +0300 (EEST) Received: by nf-out-0910.google.com with SMTP id k4so180273nfd for ; Wed, 29 Aug 2007 06:07:45 -0700 (PDT) Received: by 10.82.114.3 with SMTP id m3mr1324122buc.1188392846495; Wed, 29 Aug 2007 06:07:26 -0700 (PDT) Received: by 10.66.241.15 with HTTP; Wed, 29 Aug 2007 06:07:26 -0700 (PDT) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:mime-version:content-type; b=CZ9QIO+5UC0ZclbYqmqs89IO2AWxqJeghMU783zhJTGdMEDpiIbj6737zmC4iP7ZEj+j6hLtvYrBIA+oErYDTTzYht/2B2JHLzalRxWjtqekJD/tLV4R5mUVEg9Y8H/aZSj2YKo2h+EWE1NMHyzZgCN47wKiDEFC/g+YnG93K6k= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:mime-version:content-type; b=N2pzQqRtteBggnskinEjcQuPTbTCoymrnmzqX7y0xuc4pQUDSd+/89buaDxOK+qbTr7YT+4hgOCzCTwkk9Tr5AXUD+6MMvlDHANDZ3UkGmrwhdAkf/67SGVhCT64aIIVXJ3BcLR71HRLabzkNgNyCp5+Zh/nkMNMnAmqwWIEuXo= Date: Wed, 29 Aug 2007 16:07:26 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?Atbalsta_l=FBgums?= MIME-Version: 1.0 X-MIMETrack: Itemize by SMTP Server on RIMILT01/RIMILT/RIMI(Release 6.5.3|September 14, 2004) at 2007.08.29 16:09:02, Serialize by Router on RIMILT01/RIMILT/RIMI(Release 6.5.3|September 14, 2004) at 2007.08.29 16:09:03 Message-ID: Content-Type: multipart/mixed; boundary="----=_Part_3572_23058828.1188392846046" ------=_Part_3572_23058828.1188392846046 Content-Transfer-Encoding: base64 Content-Type: text/plain; charset=ISO-8859-13 Content-Disposition: inline TGFiZGllbiEKCjE1LnNlcHRlbWJy7iBFc3BsYW7iZOcgbm90aWtzIHN0dWRlbnR1IHBhc+JrdW1z LgoKVudsYW1pZXMgbPtndCBK+3N1IGZpbmFuc2nibG8gYXRiYWxzdHUgc2VtaW7icmEgdGVsdGlq LgoKUGllbGlrdW3iOgp4KSBQcmV6ZW504mNpamEKCi0tIApDZXLuYuIgdXogc2FkYXJi7mJ1LApB Z3JpcyBBbWVyaWtzCk1vYi50ZWwuIDIgNjQ2MTEwMQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdt YWlsLmNvbQo= ------=_Part_3572_23058828.1188392846046 X-Attachment-Id: f_f5xue1gd Content-Type: application/vnd.ms-powerpoint; name="StudentuPartijsV2.pps" Content-Disposition: attachment; filename="StudentuPartijsV2.pps"; filename="StudentuPartijsV2.pps" Content-Transfer-Encoding: base64 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAWAAAAjgoAAAAAAAAA EAAA/v///wAAAAD+////AAAAAHgKAAB5CgAAegoAAHsKAAB8CgAAfQoAAH4KAAB/CgAAgAoAAIEK AACCCgAAgwoAAIQKAACFCgAAhgoAAIcKAACICgAAiQoAAIoKAACLCgAAjAoAAI0KAAD///////// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAttachmentTruncated== ------=_Part_3572_23058828.1188392846046-- --==IFJRGLKFGIR29037UHRUHIHD-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_16.txt.eml000066400000000000000000000111721502127241700246510ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.102.1 with SMTP id e1cs84341ugm; Wed, 12 Sep 2007 22:25:35 -0700 (PDT) Received: by 10.67.103.12 with SMTP id f12mr2624608ugm.1189661135681; Wed, 12 Sep 2007 22:25:35 -0700 (PDT) Return-Path: <> Received: from blackbird.grafton.lv (blackbird.grafton.lv [159.148.13.8]) by mx.google.com with ESMTP id d26si2053796nfh.2007.09.12.22.25.32; Wed, 12 Sep 2007 22:25:35 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of blackbird.grafton.lv designates 159.148.13.8 as permitted sender) client-ip=159.148.13.8; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of blackbird.grafton.lv designates 159.148.13.8 as permitted sender) smtp.mail= Received: by blackbird.grafton.lv (Postfix) id 39C5162006D; Thu, 13 Sep 2007 08:25:32 +0300 (EEST) Date: Thu, 13 Sep 2007 08:25:32 +0300 (EEST) From: MAILER-DAEMON@blackbird.grafton.lv (Mail Delivery System) Subject: Undelivered Mail Returned to Sender To: agris.ameriks@gmail.com Auto-Submitted: auto-replied MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="EC740620064.1189661132/blackbird.grafton.lv" Message-Id: <20070913052532.39C5162006D@blackbird.grafton.lv> This is a MIME-encapsulated message. --EC740620064.1189661132/blackbird.grafton.lv Content-Description: Notification Content-Type: text/plain; charset=us-ascii This is the mail system at host blackbird.grafton.lv. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. For further assistance, please send mail to postmaster. If you do so, please include this problem report. You can delete your own text from the attached returned message. The mail system (expanded from ): maildir delivery failed: Sorry, the user's maildir has overdrawn his diskspace quota, please try again later. --EC740620064.1189661132/blackbird.grafton.lv Content-Description: Delivery report Content-Type: message/delivery-status Reporting-MTA: dns; blackbird.grafton.lv X-Postfix-Queue-ID: EC740620064 X-Postfix-Sender: rfc822; agris.ameriks@gmail.com Arrival-Date: Thu, 13 Sep 2007 08:25:29 +0300 (EEST) Final-Recipient: rfc822; jekaterina@tv5.lv Original-Recipient: rfc822;notikums@tv5.lv Action: failed Status: 5.2.3 Diagnostic-Code: X-Postfix; maildir delivery failed: Sorry, the user's maildir has overdrawn his diskspace quota, please try again later. --EC740620064.1189661132/blackbird.grafton.lv Content-Description: Undelivered Message Headers Content-Type: text/rfc822-headers Received: from hu-out-0506.google.com (hu-out-0506.google.com [72.14.214.233]) by blackbird.grafton.lv (Postfix) with ESMTP id 1B48675C04F for ; Thu, 13 Sep 2007 08:25:29 +0300 (EEST) Received: by hu-out-0506.google.com with SMTP id 28so167829hug for ; Wed, 12 Sep 2007 22:25:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:cc:mime-version:content-type; bh=L2SyivlqHxtTjH5g4+fQw5fs6kGsOHDdZAIpOVCV8D8=; b=RM2JXrCYFBhkLe4yd6MxT0vmzvsHsyA32ylCdGEs6dezOLBcyT3lgh/t+qJ6XgJpJyDA6z2lEt3GRh8UvPhjD4Kl/Y6IuiNcuzZWVAXZ7MGTBiv+3wSBCU+pyQsQDGkZWLkBTX6MgAthHhOzZAhXnyfM1Oe1Dgg0Q2UCaz9bXY4= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:cc:mime-version:content-type; b=FRTW/6SXI8LlxwFqs1q82QJK0pNGrTWJ1KVsKIqnA++p++EGtHGryiAagnHG4s91WGGZ8IeRnQhiEPDqyG3GHqpYZR3GzmsluDdLGsKhi75yYfwXq8p+XAQ9AGGMFwYpq2RCrumuHNWWnAPLFvkIQePM/4aLKJ9Ms/ORwiinMXY= Received: by 10.67.26.7 with SMTP id d7mr2639740ugj.1189661104603; Wed, 12 Sep 2007 22:25:04 -0700 (PDT) Received: by 10.67.102.1 with HTTP; Wed, 12 Sep 2007 22:25:04 -0700 (PDT) Message-ID: Date: Thu, 13 Sep 2007 08:25:04 +0300 From: "Agris Ameriks" To: Agris.Ameriks@gmail.com Subject: =?ISO-8859-13?Q?15.septembr=EE_Studentu_P=E2rtijs!?= Cc: "=?ISO-8859-13?Q?J=E2nis_Erts?=" , "Edgars Gutkis" , "Toms Beinerts" MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_8802_28973416.1189661104602" X-GRAFTON-MailScanner-Information: Scanned by GRAFTON mailscanner service X-GRAFTON-MailScanner: Found to be clean X-GRAFTON-MailScanner-From: agris.ameriks@gmail.com X-Spam-Status: No --EC740620064.1189661132/blackbird.grafton.lv-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_17.txt.eml000066400000000000000000000101311502127241700246440ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.67.22.18 with SMTP id z18cs119120ugi; Wed, 12 Mar 2008 14:31:25 -0700 (PDT) Received: by 10.82.116.15 with SMTP id o15mr221186buc.11.1205357484938; Wed, 12 Mar 2008 14:31:24 -0700 (PDT) Return-Path: <> Received: from mail.burusports.lv ([80.81.51.32]) by mx.google.com with SMTP id x6si2994337gvf.0.2008.03.12.14.31.23; Wed, 12 Mar 2008 14:31:24 -0700 (PDT) Received-SPF: neutral (google.com: 80.81.51.32 is neither permitted nor denied by best guess record for domain of mail.burusports.lv) client-ip=80.81.51.32; Authentication-Results: mx.google.com; spf=neutral (google.com: 80.81.51.32 is neither permitted nor denied by best guess record for domain of mail.burusports.lv) smtp.mail= Message-Id: <47d84bac.06c8100a.7545.ffff934bSMTPIN_ADDED@mx.google.com> Received: (qmail 8085 invoked for bounce); 12 Mar 2008 21:20:16 -0000 Date: 12 Mar 2008 21:20:16 -0000 From: MAILER-DAEMON@mail.burusports.lv To: agris.ameriks@gmail.com Subject: failure notice Hi. This is the qmail-send program at mail.burusports.lv. I'm afraid I wasn't able to deliver your message to the following addresses. This is a permanent error; I've given up. Sorry it didn't work out. : Sorry, no mailbox here by that name. (#5.1.1) --- Below this line is a copy of the message. Return-Path: Received: (qmail 8078 invoked by uid 1010); 12 Mar 2008 21:20:16 -0000 Received: from user-0c6s9jp.cable.mindspring.com (24.110.38.121) by mail.burusports.lv with SMTP; 12 Mar 2008 21:20:16 -0000 Content-Return: allowed X-Mailer: CME-V6.5.4.3; MSN Message-Id: <20080213-12430.11530.qmail@user-0c6s9jp.cable.mindspring.com> To: Subject: RE: MensHealth id 347305 From: MIME-Version: 1.0 Content-Type: text/html; charset="ISO-8859-1" Content-Transfer-Encoding: 7bit Your Alert!!
schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_18.txt.eml000066400000000000000000000101661502127241700246550ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.100.178.8 with SMTP id a8cs69023anf; Thu, 8 May 2008 23:33:43 -0700 (PDT) Received: by 10.67.119.8 with SMTP id w8mr619138ugm.34.1210314821853; Thu, 08 May 2008 23:33:41 -0700 (PDT) Return-Path: <> Received: by 10.67.119.8 with SMTP id w8mr990983ugm.34; Thu, 08 May 2008 23:33:41 -0700 (PDT) Message-ID: <00148521085105deee044cc66165@googlemail.com> From: Mail Delivery Subsystem To: Agris.Ameriks@gmail.com Subject: Delivery Status Notification (Delay) Date: Thu, 08 May 2008 23:33:41 -0700 (PDT) This is an automatically generated Delivery Status Notification THIS IS A WARNING MESSAGE ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. Delivery to the following recipient has been delayed: welcome6_@one.lv Message will be retried for 1 more day(s) Technical details of temporary failure: TEMP_FAILURE: The recipient server did not accept our requests to connect. Learn more at http://mail.google.com/support/bin/answer.py?answer=7720 [mail2.one.lv. (10): Connection dropped] [mail1.one.lv. (10): Connection dropped] ----- Message header follows ----- Received: by 10.67.119.8 with SMTP id w8mr98269ugm.34.1210139655489; Tue, 06 May 2008 22:54:15 -0700 (PDT) Return-Path: Received: from ?10.43.176.45? ( [212.93.97.166]) by mx.google.com with ESMTPS id e1sm10709566ugf.71.2008.05.06.22.53.48 (version=TLSv1/SSLv3 cipher=RC4-MD5); Tue, 06 May 2008 22:54:14 -0700 (PDT) References: <20080506173511.F23C57F1EA@mail.balticom.lv> Message-Id: <347AE19D-8217-46DB-A64C-D6043980B1C8@gmail.com> From: Agris Ameriks To: Robert Zemaitis <999881@gmail.com> In-Reply-To: <20080506173511.F23C57F1EA@mail.balticom.lv> Content-Type: multipart/alternative; boundary=Apple-Mail-1--95882711 X-Mailer: iPhone Mail (4A93) Mime-Version: 1.0 (iPhone Mail 4A93) Subject: Re: www.1mie.lv Date: Wed, 7 May 2008 08:52:29 +0300 Cc: "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "" , "<7943@inbox.lv>" <7943@inbox.lv>, "" , "" , "" , "" , "" , "" , "" , "" , "" , "" ----- Message body suppressed ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_19.txt.eml000066400000000000000000000030361502127241700246540ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.142.239.2 with SMTP id m2cs6214wfh; Tue, 21 Oct 2008 02:07:58 -0700 (PDT) Received: by 10.142.237.20 with SMTP id k20mr3599378wfh.225.1224580077680; Tue, 21 Oct 2008 02:07:57 -0700 (PDT) Return-Path: <> Received: by 10.142.237.20 with SMTP id k20mr4333056wfh.225; Tue, 21 Oct 2008 02:07:57 -0700 (PDT) Message-ID: <000e0cd32e0a87764b0459bfc48b@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Delay) Date: Tue, 21 Oct 2008 02:07:57 -0700 (PDT) This is an automatically generated Delivery Status Notification THIS IS A WARNING MESSAGE ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. Delivery to the following recipient has been delayed: 26461101@sms.id.lv Message will be retried for 2 more day(s) Technical details of temporary failure: Connection was dropped by remote host (SENT_HELO) ----- Message header follows ----- Received: by 10.142.237.20 with SMTP id k20mr2845893wfh.225.1224483056395; Sun, 19 Oct 2008 23:10:56 -0700 (PDT) Received: by 10.142.239.2 with HTTP; Sun, 19 Oct 2008 23:10:56 -0700 (PDT) Message-ID: Date: Mon, 20 Oct 2008 09:10:56 +0300 From: "Agris Ameriks" To: 26461101@sms.id.lv Subject: test MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline ----- Message body suppressed ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_20.txt.eml000066400000000000000000000026471502127241700246530ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.142.239.2 with SMTP id m2cs167913wfh; Thu, 23 Oct 2008 10:50:12 -0700 (PDT) Received: by 10.142.237.20 with SMTP id k20mr480333wfh.225.1224784212074; Thu, 23 Oct 2008 10:50:12 -0700 (PDT) Return-Path: <> Received: by 10.142.237.20 with SMTP id k20mr568444wfh.225; Thu, 23 Oct 2008 10:50:12 -0700 (PDT) Message-ID: <000e0cd32e0ae31fa20459ef4b66@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Thu, 23 Oct 2008 10:50:12 -0700 (PDT) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: 26461101@sms.id.lv Technical details of permanent failure: Connection was dropped by remote host (SENT_HELO) ----- Original message ----- Received: by 10.142.237.20 with SMTP id k20mr2845893wfh.225.1224483056395; Sun, 19 Oct 2008 23:10:56 -0700 (PDT) Received: by 10.142.239.2 with HTTP; Sun, 19 Oct 2008 23:10:56 -0700 (PDT) Message-ID: Date: Mon, 20 Oct 2008 09:10:56 +0300 From: "Agris Ameriks" To: 26461101@sms.id.lv Subject: test MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline Tests ----- End of message ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_21.txt.eml000066400000000000000000000060271502127241700246500ustar00rootroot00000000000000X-Gmail-Received: 7725338024020c2d815c02336798897c551fc80c Delivered-To: agris.ameriks@gmail.com Received: by 10.36.109.1 with SMTP id h1cs195536nzc; Tue, 7 Jun 2005 07:39:36 -0700 (PDT) Received: by 10.36.113.17 with SMTP id l17mr1524125nzc; Tue, 07 Jun 2005 07:37:56 -0700 (PDT) From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Tue, 07 Jun 2005 07:37:56 -0700 (PDT) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: kasparsramins@gmail.com ----- Original message ----- Received: by 10.36.113.17 with SMTP id l17mr1248312nzc; Tue, 07 Jun 2005 07:37:56 -0700 (PDT) Received: by 10.36.109.1 with HTTP; Tue, 7 Jun 2005 07:37:55 -0700 (PDT) Message-ID: Date: Tue, 7 Jun 2005 17:37:55 +0300 From: Agris Ameriks Reply-To: Agris Ameriks To: Kaspars Ramins Subject: Fwd: You have received photos from Adobe Photoshop Elements In-Reply-To: <20050607143108.2A7FBD96C@epsilon.bkc.lv> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_21730_14622897.1118155075685" References: <20050607143108.2A7FBD96C@epsilon.bkc.lv> ------=_Part_21730_14622897.1118155075685 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline LS0tLS0tLS0tLSBGb3J3YXJkZWQgbWVzc2FnZSAtLS0tLS0tLS0tCkZyb206IEFtZXJpWCA8YWdy aXNhQGFwb2xsby5sdj4KRGF0ZTogSnVuIDcsIDIwMDUgNTozMSBQTQpTdWJqZWN0OiBZb3UgaGF2 ZSByZWNlaXZlZCBwaG90b3MgZnJvbSBBZG9iZSBQaG90b3Nob3AgRWxlbWVudHMKVG86IEFncmlz LkFtZXJpa3NAZ21haWwuY29tCgoKIAogCgpSZWt1IGlyIGJpbGRlcyA7KSkpIAoKVGhlc2UgcGhv dG9zIHdlcmUgc2VudCBmcm9tIEFkb2JlKFIpIFBob3Rvc2hvcChSKSBFbGVtZW50cyAzLjAuIEZp bmQKb3V0IG1vcmU6IGh0dHA6Ly93d3cuYWRvYmUuY29tL3Bob3Rvc2hvcGVsZW1lbnRzd2luCgoK LS0gCkFyIGNpZfJ1LApBZ3JpcyBBbWVyaWtzClJTRCBLdWx0+3JhcyBrb21pdGVqYXMgdmFk7nTi anMKTW9iLnRlbC4gNjQ2MTEwMQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdtYWlsLmNvbQo= ------=_Part_21730_14622897.1118155075685 Content-Type: image/jpeg; name=DSC04906.jpg Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="DSC04906.jpg" /9j/4UUoRXhpZgAASUkqAAgAAAAKAA8BAgAFAAAAhgAAABABAgAHAAAAjAAAABoBBQABAAAAlAAA ABsBBQABAAAAnAAAACgBAwABAAAAAgAAADEBAgAdAAAApAAAADIBAgAUAAAAwgAAABMCAwABAAAA AgAAAGmHBAABAAAA1gAAAKXEBwAcAAAAqAgAAMQIAABTT05ZAABEU0MtVDEAAOABAAAEAAAA4AEA AAQAAABBZG9iZSBQaG90b3Nob3AgRWxlbWVudHMgMy4wAAAyMDA1OjA2OjA3IDE3OjI5OjQyAB4A moIFAAEAAABEAgAAnYIFAAEAAABMAgAAIogDAAEAAAACAAAAJ4gDAAEAAABkAAAAAJAHAAQAAAAw MjIwA5ACABQAAABUAgAABJACABQAAABoAgAAAZEHAAQAAAABAgMAApEFAAEAAAB8AgAABJIKAAEA AACEAgAABZIFAAEAAACMAgAAB5IDAAEAAAAFAAAACJIDAAEAAAAAAAAACZIDAAEAAAANAAAACpIF AAEAAACUAgAAfJIHAO4FAACcAgAAAKAHAAQAAAAwMTAwAaADAAEAAAABAAAAAqAEAAEAAACAAgAA A6AEAAEAAADgAQAABaAEAAEAAACKCAAAAKMHAAEAAAADAAAAAaMHAAEAAAABAAAAAaQDAAEAAAAA AAAAAqQDAAEAAAAAAAAAA6QDAAEAAAAAAAAABqQDAAEAAAAAAAAACKQDAAEAAAAAAAAACaQDAAEA ----- Message truncated ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_22.txt.eml000066400000000000000000000035061502127241700246500ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.181.214.19 with SMTP id r19cs228343bkq; Thu, 22 Jan 2009 10:48:41 -0800 (PST) Received: by 10.181.36.9 with SMTP id o9mr1323719bkj.148.1232650120941; Thu, 22 Jan 2009 10:48:40 -0800 (PST) Return-Path: <> Received: by 10.181.36.9 with SMTP id o9mr1741352bkj.148; Thu, 22 Jan 2009 10:48:40 -0800 (PST) Message-ID: <001636c5b669974e1b046116b89e@googlemail.com> From: Mail Delivery Subsystem To: agris.ameriks@gmail.com Subject: Delivery Status Notification (Failure) Date: Thu, 22 Jan 2009 10:48:40 -0800 (PST) This is an automatically generated Delivery Status Notification Delivery to the following recipient failed permanently: flughafentransfer@loackertours.at Technical details of permanent failure: Google tried to deliver your message, but it was rejected by the recipient domain. We recommend contacting the other email provider for further information about the cause of this error. The error that the other server returned was: 550 550 Address unknown (state 14). ----- Original message ----- MIME-Version: 1.0 Received: by 10.181.36.9 with SMTP id o9mr1323494bkj.148.1232650074912; Thu, 22 Jan 2009 10:47:54 -0800 (PST) Date: Thu, 22 Jan 2009 20:47:54 +0200 Message-ID: Subject: Travel to Lech From: Agris Ameriks To: Agris.Ameriks@gmail.com Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Hello, we are going for spring holidays to Lech. We will be travelling to Zurich or to Munich by plane. We are going to arrive on 21th of March and we are going to travel back on 28th of March. We are 5 people. How much it will cost for us to travel from: x) Zurich to Lech and back ----- Message truncated ----- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_23.txt.eml000066400000000000000000000152521502127241700246520ustar00rootroot00000000000000Delivered-To: agris.ameriks@gmail.com Received: by 10.66.241.15 with SMTP id o15cs81666ugh; Thu, 30 Aug 2007 01:16:11 -0700 (PDT) Received: by 10.86.76.16 with SMTP id y16mr222358fga.1188461771140; Thu, 30 Aug 2007 01:16:11 -0700 (PDT) Return-Path: <> Received: from pnew.exigengroup.lv (pnew.exigengroup.lv [80.233.156.204]) by mx.google.com with ESMTP id d13si451649fka.2007.08.30.01.16.09; Thu, 30 Aug 2007 01:16:11 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of pnew.exigengroup.lv designates 80.233.156.204 as permitted sender) client-ip=80.233.156.204; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of pnew.exigengroup.lv designates 80.233.156.204 as permitted sender) smtp.mail= Received: from localhost (localhost) by pnew.exigengroup.lv (X.VV.Z/X.VV.Z) id l7U8G8X9006653; Thu, 30 Aug 2007 11:16:08 +0300 Date: Thu, 30 Aug 2007 11:16:08 +0300 From: Mail Delivery Subsystem Message-Id: <200708300816.l7U8G8X9006653@pnew.exigengroup.lv> To: MIME-Version: 1.0 Content-Type: multipart/report; report-type=delivery-status; boundary="l7U8G8X9006653.1188461768/pnew.exigengroup.lv" Subject: Returned mail: see transcript for details Auto-Submitted: auto-generated (failure) This is a MIME-encapsulated message --l7U8G8X9006653.1188461768/pnew.exigengroup.lv The original message was received at Thu, 30 Aug 2007 11:16:05 +0300 from lnrixm01.exigengroup.lv [192.168.0.115] ----- The following addresses had permanent fatal errors ----- (reason: 550 Rihards_Freimanis@exigengroup.com... No such user) ----- Transcript of session follows ----- ... while talking to lnsfoh01.exigengroup.com.: >>> DATA <<< 550 Rihards_Freimanis@exigengroup.com... No such user 550 5.1.1 ... User unknown <<< 503 Issue RCPT TO: command before DATA command --l7U8G8X9006653.1188461768/pnew.exigengroup.lv Content-Type: message/delivery-status Reporting-MTA: dns; pnew.exigengroup.lv Received-From-MTA: DNS; lnrixm01.exigengroup.lv Arrival-Date: Thu, 30 Aug 2007 11:16:05 +0300 Final-Recipient: RFC822; Rihards_Freimanis@exigengroup.com Action: failed Status: 5.1.1 Remote-MTA: DNS; lnsfoh01.exigengroup.com Diagnostic-Code: SMTP; 550 Rihards_Freimanis@exigengroup.com... No such user Last-Attempt-Date: Thu, 30 Aug 2007 11:16:08 +0300 --l7U8G8X9006653.1188461768/pnew.exigengroup.lv Content-Type: message/rfc822 Return-Path: Received: from lnrixm01.exigengroup.lv (lnrixm01.exigengroup.lv [192.168.0.115]) by pnew.exigengroup.lv (X.VV.Z/X.VV.Z) with ESMTP id l7U8G5X9006650 for ; Thu, 30 Aug 2007 11:16:05 +0300 Received: from riskynew.exigengroup.lv ([192.168.0.4]) by lnrixm01.exigengroup.lv (Lotus Domino Release 6.5.5FP2) with ESMTP id 2007083011160393-377644 ; Thu, 30 Aug 2007 11:16:03 +0300 Received: from nf-out-0910.google.com (nf-out-0910.google.com [64.233.182.190]) by riskynew.exigengroup.lv (X.VV.Z/X.VV.Z) with ESMTP id l7U8MA3M025009 for ; Thu, 30 Aug 2007 11:22:10 +0300 Received: by nf-out-0910.google.com with SMTP id 4so1127851nfv for ; Thu, 30 Aug 2007 01:16:01 -0700 (PDT) DKIM-Signature: a=rsa-sha1; c=relaxed/relaxed; d=gmail.com; s=beta; h=domainkey-signature:received:received:message-id:date:from:to:subject:cc:mime-version:content-type; b=rAsdg7B2GKZQMXP9hebb2Ht3x+R4VFR8jCk292KnlSm0mlq+ARoT/lGZaBfxvK01Wf/8yGIJyuz049Khs3hNwGSpQyNUQs8fJNPRjgmi7DndbsSKxPylKkaKp6l68WAeEcH9KqxDuCpFiQ81X4WxapI0WUlZ2gNqnqtEnh5Fyno= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=beta; h=received:message-id:date:from:to:subject:cc:mime-version:content-type; b=s+jOmEvmEw7nKfTq9rj4kz6LP/oJlr4OhLO5MWWerf1EhjJ2k2RLDS1qkXKkIPpdOo5DNb2FKbWXIT/95aW7vRwmLLR9oSFCjrvS9Tnwwi7EgX2ek8VSHvbDwFxfQ3sH+dcjFEKa3uxG9Xv7bZUijYVW4WPKofFkwYeKetGXs9w= Received: by 10.78.134.2 with SMTP id h2mr72227hud.1188461760745; Thu, 30 Aug 2007 01:16:00 -0700 (PDT) Received: by 10.66.241.15 with HTTP; Thu, 30 Aug 2007 01:16:00 -0700 (PDT) Message-ID: Date: Thu, 30 Aug 2007 11:16:00 +0300 From: "Agris Ameriks" To: rihards_freimanis@exigengroup.lv Subject: =?ISO-8859-13?Q?Iel=FBgums_studentu_korpor=E2cijai?= Cc: "=?ISO-8859-13?Q?J=E2nis_Erts?=" , "Edgars Gutkis" MIME-Version: 1.0 X-MIMETrack: Itemize by SMTP Server on LNRIXM01/Eur/Srv/Exigen(Release 6.5.5FP2|October 23, 2006) at 08/30/2007 11:16:04 AM, Serialize by Router on LNRIXM01/Eur/Srv/Exigen(Release 6.5.5FP2|October 23, 2006) at 08/30/2007 11:16:06 AM Content-Type: multipart/mixed; boundary="----=_Part_5343_11195373.1188461760389" ------=_Part_5343_11195373.1188461760389 Content-Transfer-Encoding: base64 Content-Type: text/plain; charset=ISO-8859-13 Content-Disposition: inline TGFiZGllbiEKCkllbPtkemFtIEr7cyBwaWVkYWzudGllcyB1biBwcmV6ZW5053Qgc2V2aSBTdHVk ZW50IFBhcnR5IHBhc+JrdW1hCmlldHZhcm9zIDE1LnNlcHRlbWJy7iBFc3BsYW7iZOcgbm8gcGxr c3QuIDE1OjAwIC0gMTk6MDAuCgpQcmV6ZW5053Qgc2V2aSBK+3MgdmFy53NpZXQgSvtzdSB1enN0 4mTudOIgdGVsdO4sIG37c3UgaWVy4mTudOIgdmlldOIuCgpM+2R6dSBpZXBheu5zdGlldGllcyBh ciBwcm9qZWt0dSB1biBpbmZvcm3namlldCBt+3MgcGFyIEr7c3UgZGFs7mJ1CnDnYyBpZXNw52ph cyDidHLiay4KCk11bXMgYvtzIG5lcGllY2ll8GFtYSBhcu4gSvtzdSB0ZWx0cyBwcm9ncmFtbWEu CgpUZWx0cyBq4mfiZOIgcGHwaWVtLgoKS29udGFrdOdqaWV0aWVzIGFyIEVkZ2FydSBHdXRraSBl ZGdhcnMuZ3V0a2lzQGdtYWlsLmNvbSAyOTg2NTA0Ni4KCgpQaWVsaWt1beI6CnByb2pla3RzCgpQ LlMuIEphIEr7cyB6aW7idCBr4mR1IG9yZ2FuaXriY2lqdSwga3VyYSBi+3R1IGricHJlemVudOcg c3R1ZGVudGllbQp1biBrdXJhLCBpZXNw52phbXMsIGL7dHUgZ2F0YXZhIHByZXplbnTndCBzZXZp LCBkb2RhdCB6aW7idC4KCi0tCkFyIGNpZfJ1LApBZ3JpcyBBbWVyaWtzCk1vYi50ZWwuIDIgNjQ2 MTEwMQpFLXBhc3RzOiBBZ3Jpcy5BbWVyaWtzQGdtYWlsLmNvbQo= ------=_Part_5343_11195373.1188461760389 X-Attachment-Id: f_f5yza5p5 Content-Type: application/msword; name="PROJEKTS V3_2_bez.doc" Content-Disposition: attachment; filename="PROJEKTS V3_2_bez.doc"; filename="PROJEKTS V3_2_bez.doc"; filename="PROJEKTS V3_2_bez.doc"; filename="PROJEKTS V3_2_bez.doc" Content-Transfer-Encoding: base64 0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAFAAAAKgIAAAAAAAAA EAAALQIAAAEAAAD+////AAAAACUCAAAmAgAAJwIAACgCAAApAgAA//////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// AAAAAAAAAAAAAttachmentTruncated ------=_Part_5343_11195373.1188461760389-- --l7U8G8X9006653.1188461768/pnew.exigengroup.lv-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_24.txt.eml000066400000000000000000000303411502127241700246470ustar00rootroot00000000000000Return-Path: <> Delivered-To: badmail@testcompany.se Received: (qmail 2967 invoked from network); 26 Apr 2011 14:27:59 -0000 Received: from mx1.forss.net ([95.128.113.140]) (envelope-sender <>) by mail1.forss.net (qmail-ldap-1.03) with AES256-SHA encrypted SMTP for ; 26 Apr 2011 14:27:59 -0000 Received: from localhost (localhost [127.0.0.1]) by mx1.forss.net (Postfix) with ESMTP id 62B5A380087CF for ; Tue, 26 Apr 2011 16:27:59 +0200 (CEST) X-Virus-Scanned: Forss Webservice Scanner at forss.net X-Spam-Flag: NO X-Spam-Score: -1.427 X-Spam-Level: X-Spam-Status: No, score=-1.427 tagged_above=-1000 required=4 tests=[BAYES_00=-2.599, HTML_MESSAGE=0.001, MIME_BOUND_MANY_HEX=0.803, URI_HEX=0.368] Received: from customer-relay.songnetworks.se (customer-relay.songnetworks.se [195.42.210.9]) by mx1.forss.net (Postfix) with ESMTP id 7059D3800560E for ; Tue, 26 Apr 2011 16:27:57 +0200 (CEST) Received: from mail.test-receive-domain.se (mail.test-receive-domain.se [213.88.128.20]) by customer-relay.songnetworks.se (Postfix) with ESMTP id 9FEA624388 for ; Tue, 26 Apr 2011 16:27:56 +0200 (CEST) MIME-Version: 1.0 From: To: Date: Tue, 26 Apr 2011 16:27:48 +0200 Content-Type: multipart/report; report-type=delivery-status; boundary="f518d4c6-edf1-4463-b115-f1a617b52ef3" Content-Language: sv-SE Message-ID: <271e9eed-2188-497a-b051-53ad11a74673@test-receive-domain.se> In-Reply-To: <4db6d64e2f07a_1009115d44c424c@linux1.mail> References: <4db6d64e2f07a_1009115d44c424c@linux1.mail> Subject: =?iso-8859-1?Q?Olevererbart::_Testcompany_-_bekr=E4ftelse_byte_av_levera?= =?iso-8859-1?Q?nt=F6r?= --f518d4c6-edf1-4463-b115-f1a617b52ef3 Content-Type: multipart/alternative; differences=Content-Type; boundary="6377eb29-66bb-46fd-90f3-d8c649abf8d2" --6377eb29-66bb-46fd-90f3-d8c649abf8d2 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Det gick inte att leverera till f=F6ljande mottagare eller grupper: customer.testemail@test-receive-domain.se Det gick inte att hitta e-postadressen du angav. Kontrollera adressen och f= =F6rs=F6k sedan skicka meddelandet igen. Kontakta din supportavdelning om p= roblemet kvarst=E5r. Diagnostisk information f=F6r administrat=F6rer: Genererande server: test-receive-domain.se customer.testemail@test-receive-domain.se #550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ## Ursprungshuvuden: Received: from p02c12m081.mxlogic.net (208.65.145.245) by fgsrv-mail01.fasadglas.sto (192.168.0.8) with Microsoft SMTP Server id 14.0.722.0; Tue, 26 Apr 2011 16:27:47 +0200 Received: from unknown [95.128.113.150] (EHLO mail1.forss.net) by p02c12m081.mxlogic.net(mxl_mta-6.9.0-2) over TLS secured channel wit= h ESMTP id 566d6bd4.0.3897208.00-2256.4580924.p02c12m081.mxlogic.net (envelope-fro= m ); Tue, 26 Apr 2011 08:27:51 -0600 (MDT) Received: (qmail 487 invoked from network); 26 Apr 2011 14:27:28 -0000 X-Mail-System: Forss Webservice AB Mailsystem (www.forss.se) X-Virus-Scanner: Panda CommandLine Secure for Linux X-Virus-Status: No virus found X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mail2.forss.net X-Spam-Level: X-Spam-Status: No, score=3D-3.4 required=3D5.0 tests=3DALL_TRUSTED,AWL,BAYE= S_00, HTML_30_40,HTML_MESSAGE,MIME_HTML_ONLY autolearn=3Dham version=3D3.= 1.0 Received: from n-95-128-115-31.net.forss.net (HELO server1.testcompany.se) (application@testcompany.com@[95.128.115.31]) (envelope-sender ) by mail1.forss.net (qmail-ldap-1.03) with SMTP for ; 26 Apr 2011 14:27:26 -000= 0 Received: by server1.testcompany.se (Postfix, from userid 1001) id 3DD205F6= 7A; Tue, 26 Apr 2011 16:27:26 +0200 (CEST) Date: Tue, 26 Apr 2011 16:27:26 +0200 From: Testcompany To: Message-ID: <4db6d64e2f07a_1009115d44c424c@linux1.mail> Subject: =3D?UTF-8?Q?Testcompany_-_bekr=3DC3=3DA4ftelse_byte_av_leverant= =3DC3=3DB6r?=3D MIME-Version: 1.0 Content-Type: text/html; charset=3D"UTF-8" Content-Transfer-Encoding: quoted-printable X-BS-Tracker: 14771 X-BS-Id: 13362 X-Processed-By: Rebuild v2.0-0 X-Spam: [F=3D0.1428571429; B=3D0.500(0); STSI=3D0.500(-12); STSM=3D0.400(-1= 2); CM=3D0.500; MH=3D0.500(2011042613); S=3D0.200(2010122901); SC=3Dnone] X-MAIL-FROM: X-SOURCE-IP: [95.128.113.150] X-AnalysisOut: [v=3D1.0 c=3D1 a=3D_5SL2JS5Q28A:10 a=3DjPJDawAOAc8A:10 a=3DB= LceEmwcHo] X-AnalysisOut: [wA:10 a=3DIkcTkHD0fZMA:10 a=3D8JvCOHtooGmM4q1c5eVAlw=3D=3D:= 17 a=3D3G] X-AnalysisOut: [eILzi6AAAA:8 a=3DiwwpTHDTvyZOjFydv4QA:9 a=3DPf0nvSi-hewE5oC= hIv] X-AnalysisOut: [oA:7 a=3DQEXdDO2ut3YA:10] Return-Path: badmail@testcompany.se --6377eb29-66bb-46fd-90f3-d8c649abf8d2 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable

Det gick inte att l= everera till f=F6ljande mottagare eller grupper:

customer.testemail@test-receive-domain.se
Det gick inte att hitta e-postadressen du angav. Kontrollera adressen och f= =F6rs=F6k sedan skicka meddelandet igen. Kontakta din supportavdelning om p= roblemet kvarst=E5r.







Diagnostisk inform= ation f=F6r administrat=F6rer:

Genererande server: test-receive-domain.se

customer.testemail@test-receive-domain.se
#550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ##

Ursprungshuvuden:

Received: from p02c12m081.mxlogic.net (208.65.145.245) by
 fgsrv-mail01.fasadglas.sto (192.168.0.8) with Microsoft SMTP Server id
 14.0.722.0; Tue, 26 Apr 2011 16:27:47 +0200
Received: from unknown [95.128.113.150] (EHLO mail1.forss.net)	by
 p02c12m081.mxlogic.net(mxl_mta-6.9.0-2) over TLS secured channel	with ESMT=
P
 id 566d6bd4.0.3897208.00-2256.4580924.p02c12m081.mxlogic.net (envelope-fro=
m
 <badmail@testcompany.se>);	Tue, 26 Apr 2011 08:27:51 -0600 (MDT)
Received: (qmail 487 invoked from network); 26 Apr 2011 14:27:28 -0000
X-Mail-System: Forss Webservice AB Mailsystem (www.forss.se)
X-Virus-Scanner: Panda CommandLine Secure for Linux
X-Virus-Status: No virus found
X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mail2.forss.net
X-Spam-Level:
X-Spam-Status: No, score=3D-3.4 required=3D5.0 tests=3DALL_TRUSTED,AWL,BAYE=
S_00,
	HTML_30_40,HTML_MESSAGE,MIME_HTML_ONLY autolearn=3Dham version=3D3.1.0
Received: from n-95-128-115-31.net.forss.net (HELO server1.testcompany.se)
 (application@testcompany.com@[95.128.115.31])          (envelope-sender
 <badmail@testcompany.se>)          by mail1.forss.net (qmail-ldap-1.03=
) with
 SMTP          for <customer.testemail@test-receive-domain.se>; 26 Apr 2011 14:27:2=
6 -0000
Received: by server1.testcompany.se (Postfix, from userid 1001)	id 3DD205F67=
A;
 Tue, 26 Apr 2011 16:27:26 +0200 (CEST)
Date: Tue, 26 Apr 2011 16:27:26 +0200
From: Testcompany <info@testcompany.se>
To: <customer.testemail@test-receive-domain.se>
Message-ID: <4db6d64e2f07a_1009115d44c424c@linux1.mail>
Subject: =3D?UTF-8?Q?Testcompany_-_bekr=3DC3=3DA4ftelse_byte_av_leverant=
=3DC3=3DB6r?=3D
MIME-Version: 1.0
Content-Type: text/html; charset=3D"UTF-8"
Content-Transfer-Encoding: quoted-printable
X-BS-Tracker: 14771
X-BS-Id: 13362
X-Processed-By: Rebuild v2.0-0
X-Spam: [F=3D0.1428571429; B=3D0.500(0); STSI=3D0.500(-12); STSM=3D0.400(-1=
2); CM=3D0.500; MH=3D0.500(2011042613); S=3D0.200(2010122901); SC=3Dnone]
X-MAIL-FROM: <badmail@testcompany.se>
X-SOURCE-IP: [95.128.113.150]
X-AnalysisOut: [v=3D1.0 c=3D1 a=3D_5SL2JS5Q28A:10 a=3DjPJDawAOAc8A:10 a=3DB=
LceEmwcHo]
X-AnalysisOut: [wA:10 a=3DIkcTkHD0fZMA:10 a=3D8JvCOHtooGmM4q1c5eVAlw=3D=3D:=
17 a=3D3G]
X-AnalysisOut: [eILzi6AAAA:8 a=3DiwwpTHDTvyZOjFydv4QA:9 a=3DPf0nvSi-hewE5oC=
hIv]
X-AnalysisOut: [oA:7 a=3DQEXdDO2ut3YA:10]
Return-Path: badmail@testcompany.se

= --6377eb29-66bb-46fd-90f3-d8c649abf8d2-- --f518d4c6-edf1-4463-b115-f1a617b52ef3 Content-Type: message/delivery-status Reporting-MTA: dns;test-receive-domain.se Received-From-MTA: dns;p02c12m081.mxlogic.net Arrival-Date: Tue, 26 Apr 2011 14:27:47 +0000 Final-Recipient: rfc822;customer.testemail@test-receive-domain.se Action: failed Status: 5.1.1 Diagnostic-Code: smtp;550 5.1.1 RESOLVER.ADR.RecipNotFound; not found --f518d4c6-edf1-4463-b115-f1a617b52ef3 Content-Type: message/rfc822 Received: from p02c12m081.mxlogic.net (208.65.145.245) by fgsrv-mail01.fasadglas.sto (192.168.0.8) with Microsoft SMTP Server id 14.0.722.0; Tue, 26 Apr 2011 16:27:47 +0200 Received: from unknown [95.128.113.150] (EHLO mail1.forss.net) by p02c12m081.mxlogic.net(mxl_mta-6.9.0-2) over TLS secured channel with ESMTP id 566d6bd4.0.3897208.00-2256.4580924.p02c12m081.mxlogic.net (envelope-from ); Tue, 26 Apr 2011 08:27:51 -0600 (MDT) Received: (qmail 487 invoked from network); 26 Apr 2011 14:27:28 -0000 X-Mail-System: Forss Webservice AB Mailsystem (www.forss.se) X-Virus-Scanner: Panda CommandLine Secure for Linux X-Virus-Status: No virus found X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mail2.forss.net X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00, HTML_30_40,HTML_MESSAGE,MIME_HTML_ONLY autolearn=ham version=3.1.0 Received: from n-95-128-115-31.net.forss.net (HELO server1.testcompany.se) (application@testcompany.com@[95.128.115.31]) (envelope-sender ) by mail1.forss.net (qmail-ldap-1.03) with SMTP for ; 26 Apr 2011 14:27:26 -0000 Received: by server1.testcompany.se (Postfix, from userid 1001) id 3DD205F67A; Tue, 26 Apr 2011 16:27:26 +0200 (CEST) Date: Tue, 26 Apr 2011 16:27:26 +0200 From: Testcompany To: Message-ID: <4db6d64e2f07a_1009115d44c424c@linux1.mail> Subject: =?UTF-8?Q?Testcompany_-_bekr=C3=A4ftelse_byte_av_leverant=C3=B6r?= MIME-Version: 1.0 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-BS-Tracker: 14771 X-BS-Id: 13362 X-Processed-By: Rebuild v2.0-0 X-Spam: [F=0.1428571429; B=0.500(0); STSI=0.500(-12); STSM=0.400(-12); CM=0.500; MH=0.500(2011042613); S=0.200(2010122901); SC=none] X-MAIL-FROM: X-SOURCE-IP: [95.128.113.150] X-AnalysisOut: [v=1.0 c=1 a=_5SL2JS5Q28A:10 a=jPJDawAOAc8A:10 a=BLceEmwcHo] X-AnalysisOut: [wA:10 a=IkcTkHD0fZMA:10 a=8JvCOHtooGmM4q1c5eVAlw==:17 a=3G] X-AnalysisOut: [eILzi6AAAA:8 a=iwwpTHDTvyZOjFydv4QA:9 a=Pf0nvSi-hewE5oChIv] X-AnalysisOut: [oA:7 a=QEXdDO2ut3YA:10] Return-Path: badmail@testcompany.se TestCompany =20
=20 Hej Test,
Test Comapny AB vill informera dig om att vi har sett =C3=B6ver ditt avtal oc= h funnit en besparingspotential i att byta ditt avtal till=20 service provider varf=C3=B6r vi kommer att genomf=C3=B6ra detta byte i enlighet med = den fullmakt du givit oss. Test Company AB genomf=C3=B6r detta bytet i sin str=C3= =A4van att minimera dina kostnader och kontinuerligt erbjuda dig som medlem= l=C3=A4gsta m=C3=B6jliga pris.

Med v=C3=A4nlig h=C3=A4lsning

Test Company Sweden AB
Tj=C3=A4stvägen 21 A
100 10 Stockholm
=20 --f518d4c6-edf1-4463-b115-f1a617b52ef3-- schleuder-5.0.1/spec/fixtures/mails/bounces/tt_bounce_25.txt.eml000066400000000000000000000051741502127241700246560ustar00rootroot00000000000000Return-Path: <> Delivered-To: badmail@test-domain.se Received: (qmail 23861 invoked for bounce); 14 Apr 2011 15:44:12 -0000 Date: 14 Apr 2011 15:44:12 -0000 From: MAILER-DAEMON@mail1.forss.net To: badmail@test-domain.se Subject: failure notice Hi. This is the qmail-send program at mail1.forss.net. I'm afraid I wasn't able to deliver your message to the following addresses. This is a permanent error; I've given up. Sorry it didn't work out. : 65.55.37.120 does not like recipient. Remote host said: 550 Requested action not taken: mailbox unavailable Giving up on 65.55.37.120. --- Below this line is a copy of the message. Return-Path: Received: (qmail 23836 invoked from network); 14 Apr 2011 15:44:11 -0000 X-Mail-System: Forss Webservice AB Mailsystem (www.forss.se) X-Virus-Scanner: Panda CommandLine Secure for Linux X-Virus-Status: No virus found X-Spam-Checker-Version: SpamAssassin 3.1.0 (2005-09-13) on mail2.forss.net X-Spam-Level: X-Spam-Status: No, score=-3.3 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00, HTML_30_40,HTML_MESSAGE,MIME_HTML_ONLY autolearn=ham version=3.1.0 Received: from n-95-128-115-31.net.forss.net (HELO server1.test-domain.com) (application@test-domain.com@[95.115.100.31]) (envelope-sender ) by mail1.forss.net (qmail-ldap-1.03) with SMTP for ; 14 Apr 2011 15:44:11 -0000 Received: by server1.test-domain.com (Postfix, from userid 1001) id DEA9112988A; Thu, 14 Apr 2011 17:44:10 +0200 (CEST) Date: Thu, 14 Apr 2011 17:44:10 +0200 From: TestCompany To: Delar-Med-Frun@live.se Message-ID: <4da7164ad9229_c19a7f9f495573@linux1.mail> Subject: =?UTF-8?Q?TestCompany_-_V=C3=A4lkommen_som_medlem?= Mime-Version: 1.0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable TestCompany
V=C3=A4lkomme= n som medlem i Baraspara
Hej Test,
Vi tackar f=C3=B6r ett trevligt samtal och =C3=B6nskar dig varmt v=C3=A4l= kommen som medlem i TestCompany. =
=schleuder-5.0.1/spec/fixtures/mails/bounces/undeliverable_gmail.txt.eml000066400000000000000000000102661502127241700263560ustar00rootroot00000000000000 Delivered-To: saghaulor@gmail.com Received: by 10.96.89.195 with SMTP id bq3csp193052qdb; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) X-Received: by 10.236.231.244 with SMTP id l110mr3446122yhq.76.1427324797301; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) Return-Path: <> Received: from mail-yk0-x246.google.com (mail-yk0-x246.google.com. [2607:f8b0:4002:c07::246]) by mx.google.com with ESMTPS id v10si1943530yhn.71.2015.03.25.16.06.37 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 25 Mar 2015 16:06:37 -0700 (PDT) Received-SPF: none (google.com: mail-yk0-x246.google.com does not designate permitted sender hosts) client-ip=2607:f8b0:4002:c07::246; Authentication-Results: mx.google.com; spf=none (google.com: mail-yk0-x246.google.com does not designate permitted sender hosts) smtp.mail=; dkim=pass header.i=@googlemail.com Received: by mail-yk0-x246.google.com with SMTP id u21so9169ykd.2 for ; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20120113; h=mime-version:from:to:subject:message-id:date:content-type; bh=zDroqkBtnjOx4oQsUjOsgCFXb0x6mWh6blclcKQ5iYI=; b=PXS6yid56wdSx7roUQJoBggDMdNZT8cIIv5WQYV5IN4O/Vna8giENzEBwTsMyEPHBo JfVgmhO6VWJ4RFjprNQdwHtBgU6XCmGKyK7pfQmmjyukMv+goACB/ApUnmDKgzV/QzuB 3yAwRnPN4srOivQXM9l5TA5OUzoIS9rDGA+bg229WXSqU+xbsUE1hj1iCsL+1gsEqWMi m0Vpo9putaSS2Xz0SmJFq8biRKXbinBSb5d33kDp8T9P5GkRPD15byuXEKIrwR/81wXn miucJ+GuMV5Y/g7N49Nz51H6nQgG0P3VBMuQxLrNBQPb7ofnh09hsW6PwlEu0Ba2LHkS 0A3w== X-Received: by 10.140.144.11 with SMTP id 11mr14639966qhq.54.1427324797247; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) MIME-Version: 1.0 Return-Path: <> Received: by 10.140.144.11 with SMTP id 11mr12478298qhq.54; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) From: Mail Delivery Subsystem To: saghaulor@gmail.com X-Failed-Recipients: undeliverable@coupa.com Subject: Delivery Status Notification (Failure) Message-ID: <001a1137654084e059051224f354@google.com> Date: Wed, 25 Mar 2015 23:06:37 +0000 Content-Type: text/plain; charset=UTF-8 Delivery to the following recipient failed permanently: undeliverable@coupa.com Technical details of permanent failure: Google tried to deliver your message, but it was rejected by the server for the recipient domain coupa.com by aspmx.l.google.com. [2607:f8b0:400d:c03::1b]. The error that the other server returned was: 550-5.1.1 The email account that you tried to reach does not exist. Please try 550-5.1.1 double-checking the recipient's email address for typos or 550-5.1.1 unnecessary spaces. Learn more at 550 5.1.1 http://support.google.com/mail/bin/answer.py?answer=6596 140si3605502qhd.80 - gsmtp ----- Original message ----- DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:from:date:message-id:subject:to:content-type; bh=EeOLB+iPfnulBvl+QlMunNfssBotsqV19IRSrGZ7wjE=; b=cZHw/hf4sqwgBCotRSuBGMcyEViNc9PIYhV/1wVrmYZYcXsleL0b2laAQD3ri5WO67 cLQc2a2tfTRfDFTiq1JQ9MOmcRatUUBmbkydjOq8R8Xqf4bi6wJobZL0ieiT/CHJtQuk MLF/UkM10Ev/FBHFZSBnowZDhEjUzVRO4aRDHyPjzPS3+456c3ywuvb0nVDphKKMTrNW FDyaYlG01XgkgPaguknjOVpK59cSJ/aQyLBW9EoQBEgOPtkzhXbVBbiM+7ofL6DXi7lm UnMFYUkzKPnDoVlqD0d+ykjrun3a2gLD6aNXKQDynTmRpWvdzCRwsoC5UkflU5D4303U +9HQ== X-Received: by 10.140.144.11 with SMTP id 11mr14639959qhq.54.1427324797130; Wed, 25 Mar 2015 16:06:37 -0700 (PDT) MIME-Version: 1.0 From: Stephen Aghaulor Date: Wed, 25 Mar 2015 23:06:36 +0000 Message-ID: Subject: Test To: undeliverable@coupa.com Content-Type: multipart/alternative; boundary=001a1137654083206f051224f3e9 Test schleuder-5.0.1/spec/fixtures/mails/bounces/unknown_code_bounce_01.txt.eml000066400000000000000000000054461502127241700267140ustar00rootroot00000000000000Return-Path: <> Received: by 10.231.11.4 with SMTP id r4cs16374ibr; Sun, 31 Jan 2010 21:45:58 +0000 Date: Sun, 31 Jan 2010 21:45:57 +0000 From: Postmaster@tradingpost.ch To: jack@swom.com Message-Id: <4b65fa15.160db80a.5b82.ffff9cedSMTPIN_ADDED@mx.google.com> Subject: Mail Delivery Failure X-Loop-Detect: 4 Final-Recipient: rfc822;sdrake@tradingpost.ch Authentication-Results: mx.google.com; spf=pass (google.com: domain of mail03c.hostcenter.com designates 195.186.64.158 as permitted sender) smtp.mail= Delivered-To: jack@swom.com Original-Recipient: rfc822;sdrake@tradingpost.ch Received-Spf: pass (google.com: domain of mail03c.hostcenter.com designates 195.186.64.158 as permitted sender) client-ip=195.186.64.158; Delivery of email to this address "sdrake@tradingpost.ch" has failed due to a full mailbox. We have already made numerous attempts to deliver this message over the last few days. Please try to send your message again at a later time. The first 50 lines of your original message follow:> Received: from mx21.stngva01.us.mxservers.net (204.202.242.71) > by mail03c-old.hostcenter.com (RS ver 1.0.95vs) with SMTP id 4-033563664 > for ; Sun, 31 Jan 2010 02:17:45 +0100 (CET) > Received: from unknown [188.40.35.30] (EHLO xenon.swom.com) > by va1-mx21.stngva01.us.mxservers.net (mxl_mta-3.1.0-05) > with ESMTP id 73ad46b4.2654722976.745535.00-003.va1-mx21.stngva01.us.mxservers.net (envelope-from ); > Sat, 30 Jan 2010 20:17:43 -0500 (EST) > Received: from myswom.com (xenon.swom.com [188.40.35.28]) > (Authenticated sender: support@myswom.com) > by xenon.swom.com (Postfix) with ESMTPSA id 979FE3D27 > for ; Sun, 31 Jan 2010 01:16:45 +0000 (GMT) > Date: Sun, 31 Jan 2010 01:16:45 +0000 > From: Jack Whitehall > To: sdrake@tradingpost.ch > Message-Id: <4b64d9fd8f537_18e83fa070dbb1b85499c1@xenon.swom.com.tmail> > Subject: From Jack Whitehall > Mime-Version: 1.0 > Content-Type: text/plain; charset=utf-8 > X-Swom-Uuid: 257c92310bd354f1cd0f2ca4e5fd75fe50f68355 > X-Spam: [F=0.2000000000; B=0.500(0); S=0.200(2010011101); MH=0.500(2010013027)] > X-MAIL-FROM: > X-SOURCE-IP: [188.40.35.30] > X-Loop-Detect:1 > X-DistLoop-Detect:1 > > Hi Simon - Hide quoted text - > > Jack Whitehall here, co-founder of the old CashCulture program in 2003. I thought I'd reconnect with my contacts. > > So what are you up to nowadays? > > I'm working on Swom.com, a lucrative program which is very cool! Its payplan gives earnings of $10k/month. > > I've got a special VIP pre-launch link before it goes on international launch to millions on Mar 1st. If you use the link you'll get a ton of spillover. > > http://swom.com/r/34118 > > Do write back - be great to hear from you after all these years. > > = Jackschleuder-5.0.1/spec/fixtures/mails/broken_utf8_charset.eml000066400000000000000000000032701502127241700240440ustar00rootroot00000000000000Return-Path: Delivered-To: example@example.org Received: from mail.example.org (mail.example.org [192.168.1.2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by trebuchet.example.org (Postfix) with ESMTPS id 2C8A1A0403 for ; Sat, 10 Mar 2018 02:52:58 +0100 (CET) Received: from [127.0.0.1] (localhost [127.0.0.1])example.org (mx.example.org [192.168.1.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.example.org (Postfix) with ESMTPS id 7FB4610F for ; Sat, 10 Mar 2018 02:52:57 +0100 (CET) Received: from [127.0.0.1] (localhost [127.0.0.1])ng TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx2.example.org (Postfix) with ESMTPS id DA6D2880A0 for ; Sat, 10 Mar 2018 02:52:54 +0100 (CET) Received: from [127.0.0.1] (localhost [127.0.0.1])novol420-pc (unknown [192.168.1.3]) by smtp1-g21.free.fr (Postfix) with SMTP id 95E05B0051B for ; Sat, 10 Mar 2018 02:52:51 +0100 (CET) Message-ID: From: "Invitation" To: Subject: =?windows-1258?B?c29pcull?= Date: Sat, 10 Mar 2018 02:52:36 +0100 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=MailPart0000_0010_0A37C499" This is a multi-part message in MIME format. ------=MailPart0000_0010_0A37C499 Content-Type: text/plain; charset="windows-1258" Content-Transfer-Encoding: quoted-printable Hello Goodbye ------=MailPart0000_0010_0A37C499-- schleuder-5.0.1/spec/fixtures/mails/charset_mails/000077500000000000000000000000001502127241700222225ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/charset_mails/japanese.eml000066400000000000000000000005011502127241700245030ustar00rootroot00000000000000MIME-Version: 1.0 Subject: =?UTF-8?B?44G+44G/44KA44KB44KC?= From: sender@example.com To: =?UTF-8?B?44G/44GR44KL?= Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: base64 44GL44GN44GP44GI44GTCgotLSAKaHR0cDovL2xpbmRzYWFyLm5ldC8KUmFpbHMsIFJTcGVjIGFu ZCBMaWZlIGJsb2cuLi4uCg==schleuder-5.0.1/spec/fixtures/mails/charset_mails/japanese_attachment.eml000066400000000000000000000015641502127241700267250ustar00rootroot00000000000000MIME-Version: 1.0 Received: by 10.231.35.72 with HTTP; Fri, 16 Oct 2009 05:39:34 -0700 (PDT) Date: Fri, 16 Oct 2009 23:39:34 +1100 Delivered-To: schleuder@example.org Message-ID: <57a815bf0910160539m64240421gb35ea52e101aedbc@mail.gmail.com> Subject: testing From: sender@example.com To: Schleuder Content-Type: multipart/mixed; boundary=00032557395e3572cf04760cb060 --00032557395e3572cf04760cb060 Content-Type: text/plain; charset=UTF-8 testing -- http://lindsaar.net/ Rails, RSpec and Life blog.... --00032557395e3572cf04760cb060 Content-Type: text/plain; charset=UTF-8; name="=?UTF-8?B?44Gm44GZ44GoLnR4dA==?=" Content-Disposition: attachment; filename="=?UTF-8?B?44Gm44GZ44GoLnR4dA==?=" Content-Transfer-Encoding: base64 X-Attachment-Id: f_g0uxfl510 dGhpcyBpcyBhIHRlc3QK44GT44KM44KP44Gm44GZ44Go --00032557395e3572cf04760cb060--schleuder-5.0.1/spec/fixtures/mails/charset_mails/japanese_attachment_long_name.eml000066400000000000000000000045311502127241700307410ustar00rootroot00000000000000Delivered-To: schleuder@example.org Received: by 10.231.12.67 with SMTP id w3cs164325ibw; Fri, 30 Oct 2009 01:11:12 -0700 (PDT) Received: by 10.150.44.2 with SMTP id r2mr2367210ybr.77.1256890271939; Fri, 30 Oct 2009 01:11:11 -0700 (PDT) Return-Path: Received: from mx1.test.lindsaar.net.au (mx1.test.lindsaar.net.au [210.14.110.240]) by mx.google.com with ESMTP id 25si7923673gxk.34.2009.10.30.01.11.11; Fri, 30 Oct 2009 01:11:11 -0700 (PDT) Received-SPF: neutral (google.com: 210.14.110.240 is neither permitted nor denied by domain of mikel@test.lindsaar.net) client-ip=210.14.110.240; Authentication-Results: mx.google.com; spf=neutral (google.com: 210.14.110.240 is neither permitted nor denied by domain of mikel@test.lindsaar.net) smtp.mail=mikel@test.lindsaar.net Received: from [192.168.4.253] (60-241-138-146.static.tpgi.com.au [60.241.138.146]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) (Authenticated sender: mikel) by mx1.test.lindsaar.net.au (Postfix) with ESMTPSA id 5C0186DD4CD for ; Fri, 30 Oct 2009 19:11:08 +1100 (EST) Subject: =?utf-8?B?44G+44G/44KA44KB44KC44G+44G/44KA44KB44KC44G+44G/44KA?= =?utf-8?B?44KB44KC44G+44G/44KA44KB44KC44G+44G/44KA44KB44KC44G+?= =?utf-8?B?44G/44KA44KB44KC44G+44G/44KA44KB44KC44G+44G/44KA44KB?= =?utf-8?B?44KC44G+44G/44KA44KB44KC44G+44G/44KA44KB44KC?= From: sender@example.com Content-Type: multipart/mixed; boundary=Apple-Mail-6--589811753 Message-Id: <60A112A8-F26C-4E23-95B8-4EB9F139D6A0@test.lindsaar.net> Date: Fri, 30 Oct 2009 19:11:02 +1100 To: Schleuder Mime-Version: 1.0 (Apple Message framework v1076) X-Mailer: Apple Mail (2.1076) --Apple-Mail-6--589811753 Content-Disposition: attachment; filename*0*=utf-8''%E3%81%8B%E3%81%8D%E3%81%8F%E3%81%91%E3%81%93%E3%81%8B%E3%81%8D%E3%81%8F%E3%81%91%E3%81%93%E3%81%8B%E3%81%8D%E3%81%8F%E3%81%91%E3%81%93%E3%81%8B%E3%81%8D%E3%81%8F%E3%81%91%E3%81%93%E3%81%8B; filename*1*=%E3%81%8D%E3%81%8F%E3%81%91%E3%81%93.txt Content-Type: text/plain; x-unix-mode=0644; name="=?utf-8?B?44GL44GN44GP44GR44GT44GL44GN44GP44GR44GT44GL44GN44GP?= =?utf-8?B?44GR44GT44GL44GN44GP44GR44GT44GL44GN44GP44GR44GTLnR4?= =?utf-8?B?dA==?=" Content-Transfer-Encoding: 7bit this is the data --Apple-Mail-6--589811753--schleuder-5.0.1/spec/fixtures/mails/charset_mails/japanese_iso_2022.eml000066400000000000000000000003671502127241700260340ustar00rootroot00000000000000MIME-Version: 1.0 Subject: =?UTF-8?B?44G+44G/44KA44KB44KC?= From: sender@example.com To: =?UTF-8?B?44G/44GR44KL?= Content-Type: text/plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit $B$9$_$^$;$s!#(B schleuder-5.0.1/spec/fixtures/mails/charset_mails/japanese_shift_jis.eml000066400000000000000000000005661502127241700265600ustar00rootroot00000000000000Delivered-To: schleuder@example.org Date: Wed, 28 May 2014 17:18:19 +0900 (JST) From: sender@example.com To: schleuder@example.org Subject: test Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset="Shift_JIS" Content-Transfer-Encoding: 8bit ̃[̓eXgp̃[łB Ƃ낵肢\グ܂I schleuder-5.0.1/spec/fixtures/mails/charset_mails/ks_c_5601-1987.eml000066400000000000000000000004471502127241700247260ustar00rootroot00000000000000Delivered-To: schleuder@example.org Date: Wed, 28 May 2014 17:18:19 +0900 (JST) From: sender@example.com To: schleuder@example.org Subject: test Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset="ks_c_5601-1987" Content-Transfer-Encoding: 8bit Ƽ schleuder-5.0.1/spec/fixtures/mails/charset_mails/signed_utf8.eml000066400000000000000000000107131502127241700251420ustar00rootroot00000000000000Return-Path: Delivered-To: input@example.org Received: from mx-a.example.org (mx-a.example.org [10.11.12.13]) by mx-b.example.org (Postfix) with ESMTPS id B783D9 for ; Wed, 06 Mar 2020 16:11:03 +0000 (UTC) MIME-Version: 1.0 Date: Wed, 06 Mar 2020 16:11:03 +0000 From: example@example.org To: input@example.org Subject: foobar Message-ID: Content-Type: multipart/signed; protocol="application/pgp-signature"; boundary="=_3af29082653c11eaa9c874e5f9e4031"; micalg=pgp-sha256 This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --=_3af29082653c11eaa9c874e5f9e4031 Content-Type: multipart/mixed; boundary="=_7A9795606B8711EAA9CA74E5F9E4031" --=_7A9795606B8711EAA9CA74E5F9E4031 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=UTF-8; format=flowed Hi, Wie geht es dir? Danke und liebe Grüße! Input von example.org --=_7A9795606B8711EAA9CA74E5F9E4031 Content-Transfer-Encoding: 7bit Content-Type: application/pgp-keys; name=0x4DCFFC92.asc Content-Disposition: attachment; filename=0x4DCFFC92.asc; size=3813 -----BEGIN PGP PUBLIC KEY BLOCK----- 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 LBrw =8kNQ -----END PGP PUBLIC KEY BLOCK----- --=_7A9795606B8711EAA9CA74E5F9E4031 --=_3af29082653c11eaa9c874e5f9e4031 Content-Type: application/pgp-signature; name=signature.asc Content-Disposition: attachment; filename=signature.asc; size=833 Content-Description: OpenPGP digital signature -----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEEutGcDyP9I0JgSi5RsAgBRQs6K6QFAl5rnV0ACgkQsAgBRQs6 K6Rwawv9H1cIwmmwmJJq4xSXNHBcx1uoE38+0UNb4QrwvDZ4qTPxljrtzHecp0Jh ZGB1NrEyDA0ZX298eXQwyHbiGZ8BJAxG1akLmLKGpQ2CRsuy96G4Ot3nfdsVCwPD YHyxEH8YGk85BSpjl+RZpI7EUTLF3k9NB8szKEhqY8ZgdC4H2n4mCeXNLATO/6GP qFevJtFK8LAUy1S0lQQ/Y5zWpdT/GJs6MLURhtGGEiv4TLyzcRXpu04h2TBeDlT6 Tn5YfYn1sz55rbLtQo5jmD8PwNwcsElbNQiXPdL22JyinxRz6hBAVgPVMm/bWze6 K3RvzpQ0UoZrdhCgR3PZfe8cyJPvHMyy5g2IkvlN2BbdrqknCMkSyUlo/nmpMf3Z 78RP2TLOeG+JNaEmmF5LuhVRp86A3ItaMjUy1RRdqjRxoXdbqUjjcMiBMPg317Oj 9v/CJytsOXo0LHRQXdNkxPOzosUVDTPuafaqNwT1IwBpc5nKLTV2K3PS55tQ6566 EdSR1zEh =/aB0 -----END PGP SIGNATURE----- --=_3af29082653c11eaa9c874e5f9e4031-- schleuder-5.0.1/spec/fixtures/mails/charset_mails/simple_jis.eml000066400000000000000000000005041502127241700250560ustar00rootroot00000000000000Date: Wed, 28 May 2014 17:18:19 +0900 (JST) From: sender@example.com To: schleuder@example.org Subject: test Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset="Shift_JIS" Content-Transfer-Encoding: 8bit ̃[̓eXgp̃[łB Ƃ낵肢\グ܂I schleuder-5.0.1/spec/fixtures/mails/charset_mails/simple_jpiso2022.eml000066400000000000000000000003741502127241700257300ustar00rootroot00000000000000MIME-Version: 1.0 Subject: =?UTF-8?B?44G+44G/44KA44KB44KC?= From: Mikel Lindsaar To: =?UTF-8?B?44G/44GR44KL?= Content-Type: text/plain; charset=iso-2022-jp Content-Transfer-Encoding: 7bit $B$9$_$^$;$s!#(B schleuder-5.0.1/spec/fixtures/mails/charset_mails/simple_latin1.eml000066400000000000000000000003361502127241700254640ustar00rootroot00000000000000Subject: test To: schleuder@example.org From: sender@example.com Date: Wed, 6 Nov 2019 22:04:23 -0200 MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859 Content-Language: en-US Content-Transfer-Encoding: 8bit schleuder-5.0.1/spec/fixtures/mails/charset_mails/simple_utf8.eml000066400000000000000000000003341502127241700251600ustar00rootroot00000000000000Subject: test To: schleuder@example.org From: sender@example.com Date: Wed, 6 Nov 2019 22:04:23 -0200 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 8bit é schleuder-5.0.1/spec/fixtures/mails/charset_mails/thunderbird-multi-alt-html.eml000066400000000000000000000067121502127241700301110ustar00rootroot00000000000000From: foo bar To: input@example.org Subject: =?UTF-8?Q?input_f=c3=bcr_Test?= Message-ID: <435093879857398573985735@example.org> Date: Fr, 12 Mar 2020 09:49:01 +0500 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:60.0) Gecko/20100101 Thunderbird/60.9.1 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="------------421BD2D66A2A11EAB33E74E5F9E4031" This is a multi-part message in MIME format. --------------421BD2D66A2A11EAB33E74E5F9E4031 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable liebes example.org team, Wie geht es euch? Wir haben uns schon Gedanken gemacht: example.org danke für euren Support! gr=C3=BC=C3=9Fe foo bar --------------421BD2D66A2A11EAB33E74E5F9E4031 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 8bit

liebes example.org team,

Wie geht es euch?

Wir haben uns schon Gedanken gemacht: example.org

danke für euren Support!

grüße

foo bar

--------------421BD2D66A2A11EAB33E74E5F9E4031-- schleuder-5.0.1/spec/fixtures/mails/containing-pgp-garbage.txt000066400000000000000000000012341502127241700244500ustar00rootroot00000000000000Date: Sat, 4 Jan 2020 23:42:49 +0000 From: To: schleuder@example.org Subject: Test Message-ID: <20200104234249.GB9231@example.org> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="eAbsdosE1cNLO4uF" Content-Disposition: inline --eAbsdosE1cNLO4uF Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --eAbsdosE1cNLO4uF Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- hF4DTO8GH8gtgSMSAQdAjiYMTVwKw70Z3H8NwyZeHpPopnE1BB2L8Cs0MF95AXYw =xJbn -----END PGP MESSAGE----- --eAbsdosE1cNLO4uF-- schleuder-5.0.1/spec/fixtures/mails/encapsulated.eml000066400000000000000000000046661502127241700225670ustar00rootroot00000000000000From admin@a Wed Jun 09 16:14:08 2021 From: admin@a To: list@a Subject: test Date: Wed, 09 Jun 2021 18:14:08 +0200 Message-ID: <6066403.5e4LmiuuCV@deepthought> MIME-Version: 1.0 Content-Type: multipart/encrypted; boundary="nextPart2013499.KfxGTPaf5f"; protocol="application/pgp-encrypted" --nextPart2013499.KfxGTPaf5f Content-Type: application/pgp-encrypted Content-Disposition: attachment Content-Transfer-Encoding: 7Bit Version: 1 --nextPart2013499.KfxGTPaf5f Content-Type: application/octet-stream Content-Disposition: inline; filename="msg.asc" Content-Transfer-Encoding: 7Bit -----BEGIN PGP MESSAGE----- hQIMAx0nShUMaPCRAQ//f0l0D6hTAYsNxuD5dOCSdio6pniKNXWu+bJnZQG1eGEs 4tG2NreH7VpX9qCDd9ZXb4Dh2tFyhVS0NO/nza/bOjqwI7luLlr7QbaxSemyY06+ d7GVITKVbo/d/iO5UyK8MyOESyLe/u3LydsDKm9aJP/sYjtjatuW9iQyEy9KLkTi 8v2IjAMlK28lcuX0PiXOelU+V6jg5humUT11GWmC3iP7IjUxaPMbdbUNdqnZ2CI3 ZW2N50/AF6CS4laRG1xawNy5c3TyoBR/Owd5tHvjGN7PMTSPE99K6VETMV+RfK3n ouPYm952mSybmIkZE/uU9SvfPS0vYxAgo+BcnfzpHEKAm5UpEBK3LZ7AJizaCOXK LAYkK6dR2/38jCLjGm187p2IiJGy57AT2e4rsSXTqW9ywz0ukTC3xzGKuq9NPAu4 LH6dXvEutmFBo1SWObEksYSOmPzCOdcknWl5xhlgPBMzG1mOXaWgkogITl2F/Cta vuWGSuOZLo+3WEmpSYGKddrrQM0AiWMlPN/Xjh6icIyxNzjFTYP5iRdDCqlGQEPV s9AAhYDB0sPYDyd03JSAdZrrZOCKu8ezW8KAsiYLoypfTGSbqvLeWPXyAAZcoFTX dhdZoSPiKsdb9MQ1saAgPS+Gt6RKu/i2tV2ZRhlgUi7VQWeiSPawS8poyNurY3GE jAMCZeuxTV+KPgEEAIlDW20JIWC/3DQ6j2GJfzosfpYb2FBycohIpJawmvf5b5hH hTWGwrYlSBrA3xIiC3EFc9EgCJ8vhicDe+xD5dmKywGS7xRqU2a+hNp8k/CA6FLZ FO4rGtwBGRNor/DN20nwx6PEC9baD0W0SPwqt2CHi6XRomyyMfCarQ7XiA4Y0ukB rPf+FIIuhYLFRZs1Od6s4uatPZxAjh8yZQ6GAiJ7H2BaT5vERBIXvWDqrGQmA44Q t35Ey2MFIIx1KQyaeHthlE++loPSEtzSerV+4pAqEz+NiCv4BOxybNHk9S9evOZo k6fuiLBCw6ezDzSgvQbxVrO3q0wNx2KAe0WWunaVaebQ2iTsOiVKhsL1XANmdta6 rA4avZHHsf+OAFbSyWG9k0tb5PBeQJZCqctKbVgyL2psWDpgHLYkYbMPH58RV1W8 8nM83o1ecSDqswVrUYWVNiTYXKeZf/FUbJJUd996EL7NTm3Y610EtaAXOmmGnjKc rRnCILQiRHc83/tPMw57qMN7Zf2cwZUU8iPuxnJxlOzapppPrOHA9vllGYj73uxu MKYJvNpar+pQ/HeyWaz0xlwGLn4qpTUkCVtul0YhsIaErrZi1hNIGlxueYeOtCdr Z5VOvxvEgLxUnUQwdH5re65xM0cLQaMIDYbOkyAtILUoHEaO0hal76XaI7Ene3wL iPP7RLIasdj6PgeupuX8pzsGbFnlZatC4jrtoO807k0AYDNpd6RmRzwCONbm3fkH Hg/ethyjqxwsJjcF65ckW+ofTWgtuFsheujqNQuJ97NAFZPssxPcPLjP94vt9R+J uXF7bdBqn03qCcMJNJE8p7SPJzhbbkcm2BumTcZec5dMWY4x/IBfJS2vtzJ8RKAw hZ4EWKsg7t8uXqKyPWuOy+5dOZbm4AyP7TB9jaLN82Jtn+MAy4VDskR+vIa7Gn4Y ZOHRVMe9lpyLZ1R7yXmO+v/nBAoStBt2jZ2VvLH5SQEIaBmfqxAtZksONhs8nKNo 83SPmpzacXmdbQELy+bRqrgxWTuapmmCI7cMArdXyGxXOzIoYCiV =Q/1n -----END PGP MESSAGE----- --nextPart2013499.KfxGTPaf5f-- schleuder-5.0.1/spec/fixtures/mails/encrypted+signed-inline/000077500000000000000000000000001502127241700241225ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/encrypted+signed-inline/thunderbird.eml000066400000000000000000000072571502127241700271460ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id 63585801DD for ; Thu, 29 Dec 2016 15:17:07 +0100 (CET) To: schleuder@example.org From: paz Subject: encrypted+signed-inline test Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: <216c8188-a773-fa94-d2e1-b2db4a309fed@nadir.org> Date: Thu, 29 Dec 2016 15:17:07 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit -----BEGIN PGP MESSAGE----- Charset: utf-8 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 IRNo4dJ6xTzOYXQD0Wo+z5kV =gl5b -----END PGP MESSAGE----- schleuder-5.0.1/spec/fixtures/mails/encrypted+signed-mime/000077500000000000000000000000001502127241700235735ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/encrypted+signed-mime/thunderbird.eml000066400000000000000000000112001502127241700265760ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id 1DCDC801DD for ; Thu, 29 Dec 2016 15:09:43 +0100 (CET) To: schleuder@example.org From: paz Subject: encrypted+signed-mime test Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: Date: Thu, 29 Dec 2016 15:09:29 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9 Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9 Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 xdiqE90= =FY19 -----END PGP MESSAGE----- --V4nVnp5nddnPgg4bACDcs1kSu5wBuHpL9-- schleuder-5.0.1/spec/fixtures/mails/encrypted-inline/000077500000000000000000000000001502127241700226555ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/encrypted-inline/thunderbird.eml000066400000000000000000000056141502127241700256740ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id C4826801DD for ; Thu, 29 Dec 2016 15:19:35 +0100 (CET) To: schleuder@example.org From: paz Subject: encrypted-inline test Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: Date: Thu, 29 Dec 2016 15:19:35 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit -----BEGIN PGP MESSAGE----- Charset: utf-8 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 gIXCntFc =7wno -----END PGP MESSAGE----- schleuder-5.0.1/spec/fixtures/mails/encrypted-mime/000077500000000000000000000000001502127241700223265ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/encrypted-mime/thunderbird.eml000066400000000000000000000074271502127241700253510ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id DAFE5801DD for ; Thu, 29 Dec 2016 15:13:34 +0100 (CET) To: schleuder@example.org From: paz Subject: encrypted-mime test Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: <51db1a44-b9b7-1390-b8ce-a0980101842d@nadir.org> Date: Thu, 29 Dec 2016 15:13:34 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="9JN8w93dvNGWNV5ifxvV0OtHWs5PxOc8A" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --9JN8w93dvNGWNV5ifxvV0OtHWs5PxOc8A Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --9JN8w93dvNGWNV5ifxvV0OtHWs5PxOc8A Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 =jj1e -----END PGP MESSAGE----- --9JN8w93dvNGWNV5ifxvV0OtHWs5PxOc8A-- schleuder-5.0.1/spec/fixtures/mails/encrypted-to-absent-key.txt000066400000000000000000000023661502127241700246310ustar00rootroot00000000000000Date: Sat, 4 Jan 2020 23:42:49 +0000 From: To: schleuder@example.org Subject: Test Message-ID: <20200104234249.GB9231@example.org> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="eAbsdosE1cNLO4uF" Content-Disposition: inline --eAbsdosE1cNLO4uF Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --eAbsdosE1cNLO4uF Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- hQGMA+8L5wLeonReAQv/ch+gZBhnP0ikFnkmEXB50e3oWlP7/Gc0hRetb6s5I8u4 dJOt9FUpaWiKg/OryBafJ889VtE7ujO2d8T+D0xwqiE9JRNzP3DiWiXgNpKw5pmN L6R4+skZ9UK009AzxWSivgg6fJIpk8i022n9c5DPK4sMOE2jUYV7C2BMzXcSq8jf vr5Wet3lf2yzfU+Pb62s8N1y/qg0PZrJb/0ddnaY4DDMPb4db3H2fuOEzuHnyxOK WiphCGWSKqKUUWt/x/01GABWBDCy/5HB5ow1o43u8KDriTVB765dd0aS2QGqhLD8 yvhEnzjb1DRz5Bml7NOXKeueFL80S0vge1AKT1YOf1bW1YfTEpn0jiLr8zasd41c c7cM6SGX6PFV6xGTqMgHLBZaN7Xj4ijgqZfPslYJp4iqjyQ18y1S3zMBDLc3s+9T iihTi2Ve2D2nI7Xb1Cl/UAgRDin7pQT39UBsHtrKHbpf0NTKYQKzHk+0K+VZn1Bq a89bFZrfiT+iMXovAzx20loBzk6GUrmSRSgngW7ai0se4nEQ7Vj8xJV/awmBq8Oz O8UtXiQlIoqOXH07aLxZm819BRNy66XiRUsW0bxN/pzSbOlhivIXYm5ypy5UWEsl hGHDxswJibLDdpc= =pSCD -----END PGP MESSAGE----- --eAbsdosE1cNLO4uF-- schleuder-5.0.1/spec/fixtures/mails/encrypted-to-passphrase.txt000066400000000000000000000013461502127241700247350ustar00rootroot00000000000000Date: Sat, 4 Jan 2020 23:42:49 +0000 From: To: schleuder@example.org Subject: Test Message-ID: <20200104234249.GB9231@example.org> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="eAbsdosE1cNLO4uF" Content-Disposition: inline --eAbsdosE1cNLO4uF Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --eAbsdosE1cNLO4uF Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- jA0ECQMCk858png0tIj/0lQBvHbaDcW9nElJfL0tgFhEnZv3847kDXaRpYAaQhAY TmHzD51Z7uUbalJSxgmLnBVFRFop2sKSmba1sqtlhszYFjTRerlWAJsb5vNZ1KIB 1kyOuBU= =42m3 -----END PGP MESSAGE----- --eAbsdosE1cNLO4uF-- schleuder-5.0.1/spec/fixtures/mails/exchange.eml000066400000000000000000000051521502127241700216700ustar00rootroot00000000000000From: someone@example.org To: schleuder@example.org Message-ID: <098234lkj098324lkajsdfklj09er09a842d@localhost> Date: Thu, 01 01 2017 12:13:14 +0100 Subject: hotmail mangled message Content-Type: multipart/mixed; boundary="_003_MIME_" MIME-Version: 1.0 X-OriginatorOrg: hotmail.com X-MS-Exchange-CrossTenant-originalarrivaltime: 29 May 2017 00:00:00.0000 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Internet X-MS-Exchange-CrossTenant-id: dajfkajfkljaklfj X-MS-Exchange-Transport-CrossTenantHeadersStamped: HEADERSTAMP --_003_MIME_ Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable --_003_MIME_ Content-Type: application/pgp-encrypted; name="PGPMIME version identification" Content-Description: PGP/MIME version identification Content-Disposition: attachment; filename="PGPMIME version identification"; size=12; creation-date="Mon, 29 May 2017 00:00:00 GMT"; modification-date="Mon, 29 May 2017 00:00:00 GMT" Content-ID: Content-Transfer-Encoding: base64 VmVyc2lvbjogMQoK --_003_MIME_ Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message.asc Content-Disposition: attachment; filename="encrypted.asc"; creation-date="Mon, 29 May 2017 00:00:00 GMT"; modification-date="Mon, 29 May 2017 00:00:00 GMT" Content-ID: Content-Transfer-Encoding: base64 LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tCgpoUUlNQTY5MVg4R2wyTUFyQVEvL1d3SEx1Nlp2 WE15M0VkemlaYUZnaytqQ2NxOHZXS3dTc1FQS25zWTdSRWxKCkpsdENta1JSZ2d0ajVDbDNaWWIv eVhLVEhNUElUd0hnQ3F3MVFhVFpIT2ZCT3FET3lyWVJndExEWHpabXd3RnQKQVdSWmVDWndQenJp OElKbmJaMHdlaUhMRzFiMzR2c1pqc2pIN1E3dHd4clRVbEFmbTJuUXY3TU9naFpibFRKQwpOUlY5 ekdSM0lNa0JEbURhMy9kUkRkam5kaG02dFJxMVFNS1FjdUNYekpScHUwQlNuMFFacnFHcitKdmQz TnN1CjVqTlR4dHQyV3Z6d296eHdHbVltckZ4U3E3L2VXdjdTWnFEMWFicEFQY0RrQmxIdkdobi9a Z29sZUovVGJKb2oKaWpRb1RzUDQ5YnFWUm5HRUtFK3RWVmt5dmpmeFgrTDNIUGpVdjVheFpoRFRp ejNsQnZBWWRTZlZ4cHFDWTVHMApDVWFJNFlxb3lEUlY3a2UyY0FxOFV5ODA2akFmYkUzMi9qUGlQ VkxjUDROem9WRGVyRkZ5NDBVMFp6cGtLL2pCCnpNdUVxZE9nd09xZUtHdkZCMEd5OWFXcVZYUWJZ Y1FoZ0dRV3Jxck9Jb1pTbmt3SnlhSjdqNXIvV0tFMDZuRVUKdEVXcEFEWDlHVlRFL1EyL0ZJMzB6 UCtzMnU2S2FLclB5dXZORTZXMk1abFNZMFUzaVFtY1dwQlFQZUZGOEhhYQpMM1hvMy9SdVFCRlBX S2hWTzh4ZlAxRllVc2lnallYcjJIcGpXaVdBejkxVG00MC95RjRObWN0STZEejFzaExTCkI2aGds Z2hneFZEdkZIcUprREVlRGk2R1lsTTVaaGg0WklpeDBlVWFjcGRybVM3TUZaenhzMjA0T3prQ21n UFMKV1FHbWV6NWlvNmxtbUZLZlhWYXJOajBwVHZHOVUzekJGUUozWldEUUZDZCtidGtrUm5ndWhH K3NiN2taZmkrTAo1U1ZqZDNWRGRReTU5TlNIbzk4Ukl2dEtIZlhnU0l1WU5Lc2pDTHpOcFhMNGM0 WGYzY0U0ZndNWQo9VXU1SAotLS0tLUVORCBQR1AgTUVTU0FHRS0tLS0tCg== --_003_MIME_ schleuder-5.0.1/spec/fixtures/mails/exchange_no_parts.eml000066400000000000000000000030071502127241700235720ustar00rootroot00000000000000From foobar@outlook.com Wed Jul 12 10:40:53 2017 Received: from bla.eurprd02.prod.outlook.com ([fe80::dead:beef]) by bar.eurprd02.prod.outlook.com ([fe80::dead:beef%42]) with mapi id 00.00.0000.00; Wed, 12 Jul 2017 00:00:00 +0000 From: Foo Bla Bar To: "schleuder@example.org" Subject: aSubject Thread-Topic: aSubject Thread-Index: jafkjakfjaklfjkljklj Date: Wed, 12 Jul 2017 00:00:00 +0000 Message-ID: Accept-Language: de-DE, en-US Content-Language: de-DE X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: example.org; dkim=none (message not signed) header.d=none;example.org; dmarc=none action=none header.from=outlook.com; x-incomingtopheadermarker: OriginalChecksum:somechecksum;UpperCasedChecksum:anotherchecksum;SizeAsReceived:6666;Count:23 x-ms-exchange-messagesentrepresentingtype: 1 x-tmn: [akfjkafjkajf+ajkfhajkfhjhkjfahfjk] x-ms-publictraffictype: Email x-incomingheadercount: 23 x-eopattributedmessage: 0 x-exchange-antispam-report-test: UriScan:; spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM Content-Type: text/plain; charset="us-ascii" Content-ID: Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-OriginatorOrg: outlook.com X-MS-Exchange-CrossTenant-originalarrivaltime: 12 Jul 2017 00:00:00.0000 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Internet X-MS-Exchange-CrossTenant-id: aaaa-bbbb-cccc-dddd-aaaaaaaaaaaa X-MS-Exchange-Transport-CrossTenantHeadersStamped: DEADBEEF bla-vla lu du schleuder-5.0.1/spec/fixtures/mails/mail_with_pgp_boundaries_in_body.txt000066400000000000000000000005151502127241700267070ustar00rootroot00000000000000To: list@example.org From: test@example.org Subject: Test Message-ID: <8db04406-e2ab-fd06-d4c5-c19b5765c52b@example.com> Date: Tue, 27 Dec 2016 17:18:57 +0100 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Test -----BEGIN PGP MESSAGE----- Only to show what is between here... -----END PGP MESSAGE----- schleuder-5.0.1/spec/fixtures/mails/multipart-alternative/000077500000000000000000000000001502127241700237415ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/multipart-alternative/thunderbird-multi-alt-signed.eml000066400000000000000000000052101502127241700321250ustar00rootroot00000000000000To: paz@nadir.org From: paz Subject: multi/alt test signed Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: <8aff12ad-3045-a69c-bdcc-f1638c699623@nadir.org> Date: Sun, 12 Feb 2017 22:26:47 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.7.0 MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="b3ViXpO41teSXWPCLNnj2VCalcqUBwURw" This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --b3ViXpO41teSXWPCLNnj2VCalcqUBwURw Content-Type: multipart/mixed; boundary="wq1LskS7mbBfokmGQFKSgUaVwAn5TCnAA"; protected-headers="v1" From: paz To: paz@nadir.org Message-ID: <8aff12ad-3045-a69c-bdcc-f1638c699623@nadir.org> Subject: multi/alt test signed --wq1LskS7mbBfokmGQFKSgUaVwAn5TCnAA Content-Type: multipart/alternative; boundary="------------9168A8DB20220BC208511980" This is a multi-part message in MIME format. --------------9168A8DB20220BC208511980 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable _*This is a test.*_ --------------9168A8DB20220BC208511980 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable

This is a test.

--------------9168A8DB20220BC208511980-- --wq1LskS7mbBfokmGQFKSgUaVwAn5TCnAA-- --b3ViXpO41teSXWPCLNnj2VCalcqUBwURw Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEUlB7AWOo2fAJT/4DsaNvCAaeVd4FAlig0xoACgkQsaNvCAae Vd7QzQ//a3DZKrIJeABLD/RfB4rZVmxat+kJAjsNKSnJk7sfxrtcCsDTlNY2QGGt SEe8f3HtpcOjFMVqH7FVXSaXM+E/emmPP19js2c2uLVTZi9SLkvF+NX/4dOXq0nR 6sVwEokN0/dTu4aV8cdTenKFUdIJzAjpfWgAPFOmyCe2KDSzyLrkmYbtBgSqO6l3 3S5rk4AqVVYrklWp50C+oKxVyE57Rs9848YOn3awRvFT+aocdH+UI74h9PD9Hx8z OhaT9qNEJ/KJGYjjCRv4Ls5zbKHok4RCuFozRU9UEvBDzT/NzWnMLWL90eSh4akV U/MMLGRSaB27+KBLRuxsre/eSQt6cCQWDGZbkf4VLKTEHtIKWnNPRXzSUb9SQ4xa zjHqKDVBZmHW8ftqwWK2fU1EGa9cAOpBqFyIdA/XPRX6w4n0r/Wo+ZVRtVPBiZEv K0SMNpXidfo07MgC9urEvPZi8Bo5EqwZYSpVUZsLQSWKvkw+n/+APzIaXyjP1DNs 4yFc4pqAErtokiftjT2r4P3gCXDTAOJBzHP9e1FzMS4rW9dLw+aa7FyWUvXqiRzd F0X/Ah8f8asr9Tpt6uoDYpRZ3Ny3R4/Gr0U9CzqBFbg0PfN1PJmOl2wGc6giIiAx Qf3wFp+7EmQJzlfLdEKyb4Nl5n2ColDVcD6r1pA4QNuh0vdgwYs= =7nKg -----END PGP SIGNATURE----- --b3ViXpO41teSXWPCLNnj2VCalcqUBwURw-- schleuder-5.0.1/spec/fixtures/mails/multipart-alternative/thunderbird-multi-alt-unsigned.eml000066400000000000000000000024141502127241700324730ustar00rootroot00000000000000Return-Path: X-Original-To: paz@duckdalbe.org Delivered-To: paz@duckdalbe.org Received: from flutsch.local (x4dbbbe7a.dyn.telefonica.de [77.187.190.122]) by duckdalbe.org (Postfix) with ESMTPSA id DC98332097 for ; Sun, 12 Feb 2017 22:27:50 +0100 (CET) From: paz Subject: multi/alt test unsigned To: paz@nadir.org Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: Date: Sun, 12 Feb 2017 22:27:48 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.7.0 MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="------------995F058E0A221863BAC4E3E7" This is a multi-part message in MIME format. --------------995F058E0A221863BAC4E3E7 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit _*This is a test.*_ --------------995F058E0A221863BAC4E3E7 Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: 7bit

This is a test.

--------------995F058E0A221863BAC4E3E7-- schleuder-5.0.1/spec/fixtures/mails/mutt-2.0.5-linux-xaddkey-attachment-binary.eml000066400000000000000000000116201502127241700277300ustar00rootroot00000000000000Date: Wed, 1 Nov 2023 16:37:18 +0000 From: user@example.org To: schleuder@example.org Subject: test Message-ID: <950124.162336@example.org> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="K5LL32V+kDX3P/yR" Content-Disposition: inline --K5LL32V+kDX3P/yR Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --K5LL32V+kDX3P/yR Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- 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 =nHjy -----END PGP MESSAGE----- --K5LL32V+kDX3P/yR-- schleuder-5.0.1/spec/fixtures/mails/mutt_protected_headers.txt000066400000000000000000000033621502127241700247060ustar00rootroot00000000000000From schleuder@example.org Thu Jun 13 15:19:33 2019 Received: from 127.0.0.1 (helo=localhost.localdomain) by mail.example.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) id 1hbPdc-0007GN-6b for schleuder@example.org; Thu, 13 Jun 2019 15:19:32 +0200 Date: Thu, 13 Jun 2019 15:19:30 +0200 From: dev To: schleuder@example.org Subject: ... Message-ID: <20190613131930.ABC@xyz> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="z6Eq5LdranGa6ru8" Content-Disposition: inline --z6Eq5LdranGa6ru8 Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --z6Eq5LdranGa6ru8 Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- hQIMA691X8Gl2MArAQ//SFZyc/TD/9PYMddJcUIp4F85wsoCUZUaVLpKBzUZdrLv rln9bgaou4MiUXF8ZTSqq2ET6A3X7+wpDjs79KiDJnILUmguGDT2KTkyD8lxP9nd oIKtqKdf95AYGmItYkaQqdZf1No2q4ZBQNWXp8+LZgxINn5AW+9wuOo8F9w+tyZJ 8r9jlj5TJ0YnVp5FieKMMyxiSOCGX8lAaqi4TbML35OWrnL8Decsz5tTX4jfqr8L cvNuIpa863WkbZxMxLEEn4/yC6upmOnU3eSZ9M/UoXiqgBsd01KEoOvmIIPOgGce IaCxO4zuoPvtcQsuinlLCI2oX9mpex6iTMGmD1J0G9FNGI3OHkwZcahw+4/3dv9K jfUjm6XwndtYi6ifAPAf8M8RT84hFlZKqR7IpGmpqWnLZx6BcFV0RDu8GCIPD6Fr UeLu1hGLD3SMbKy9zSR4lDSkMRvCUumXAebtEvfp7dfQ9Z8I866J5/9EZIDH88M1 Rb9agaBlwwr8Oy0hzC3rwvLyqXi1KD79f+YmGL0yatYPTm37qCE+QdfXCkesN6jg SV3zjtpBalP0KMCtAhouFf6xDz615nWvC5NRh2yzYOhSVfmZEVrB9Zz7GZx8rsMi 2U0ALYJIc6EI0uc/sLZ9dYu6hBa72VmSe90zS5IE2ZYB24GnzXV95iMsvH35/4vS igFWNQmHxWc9GfwgXN2/P2k4zokCxywLLhoa5xoy+SGmipz2pU8IS4chGT6H5edU /CdBAAdHw7eNgXkuctH9eAM7WE/yu+1tmAPUvzr3ojFyXF+L8d8gcCbOJA4scHb6 /L8B0J+CZG/dkMDxU/xjyc7zz8not13HTr6xklMUIam/OzM95YFmleAN4g== =l547 -----END PGP MESSAGE----- --z6Eq5LdranGa6ru8-- schleuder-5.0.1/spec/fixtures/mails/not_bounces/000077500000000000000000000000001502127241700217225ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/not_bounces/Bug_948980-_marked_as_pending_in_schleuder.eml000066400000000000000000000156501502127241700323250ustar00rootroot00000000000000Received: from immer7-21.glei.ch ([192.168.1.91]) by immer12-24.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) id 1irm4e-0006qg-IY for team@schleuder.org; Wed, 15 Jan 2020 18:03:20 +0100 Received: from localhost ([127.0.0.1]) by immer7-21.glei.ch with esmtp (Exim 4.92.3) id 1irm4e-0008VW-1F for team@schleuder.org; Wed, 15 Jan 2020 18:03:20 +0100 Received: from immer7-21.glei.ch ([127.0.0.1]) by localhost (immer7-21.glei.ch [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id BsQ6Lnw6HFJD for ; Wed, 15 Jan 2020 18:03:14 +0100 Received: from mailly.debian.org ([82.195.75.114]) by immer7-21.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.3) id 1irm4Y-0008UB-8q for team@schleuder.org; Wed, 15 Jan 2020 18:03:14 +0100 Received: from ticharich.debian.org ([2607:f8f0:614:1::1274:64]:40110)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=ticharich.debian.org,EMAIL=hostmaster@ticharich.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4S-0000Ua-Eq=0D=0A for team@schleuder.org; Wed, 15 Jan 2020 17:03:08 +0000 Received: from localhost ([::1]:49224 helo=ticharich.debian.org) by ticharich.debian.org with esmtp (Exim 4.92) (envelope-from ) id 1irm4R-00088i-2J for team@schleuder.org; Wed, 15 Jan 2020 17:03:07 +0000 Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72]:51344)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)=0D=0A by ticharich.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4Q-000886-SQ=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:06 +0000 Received: from quantz.debian.org ([2607:f8f0:614:1::1274:73]:52730)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=quantz.debian.org,EMAIL=hostmaster@quantz.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4P-0000U6-Dj=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:05 +0000 Received: from qa by quantz.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4O-00028a-4N for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:04 +0000 Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39]:51042)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=buxtehude.debian.org,EMAIL=hostmaster@buxtehude.debian.org (verified)=0D=0A by quantz.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4O-00027y-2S=0D=0A for schleuder@packages.qa.debian.org; Wed, 15 Jan 2020 17:03:04 +0000 Received: from debbugs by buxtehude.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4N-0007aH-MJ; Wed, 15 Jan 2020 17:03:03 +0000 Received: via spool by 948980-submitter@bugs.debian.org id=U948980.157910757827991 (code U ref 948980); Wed, 15 Jan 2020 17:03:02 +0000 Received: (at 948980-submitter) by bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72]:55084)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)=0D=0A by buxtehude.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm14-0007HD-1p=0D=0A for 948980-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from godard.debian.org ([209.87.16.44]:52232)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=clientcerts/godard.debian.org,EMAIL=hostmaster@clientcerts/godard.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm12-0000MR-EO=0D=0A for 948980-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:36 +0000 Received: by godard.debian.org (Postfix, from userid 3510) id D6CD1A0EC0; Wed, 15 Jan 2020 16:59:34 +0000 Resent-Date: Wed, 15 Jan 2020 17:03:02 +0000 Resent-From: Debian BTS Resent-To: Georg Faerber Resent-Message-ID: Date: Wed, 15 Jan 2020 16:59:34 +0000 From: Georg Faerber Sender: noreply@salsa.debian.org Reply-To: Georg Faerber , 948980-quiet@bugs.debian.org To: 948980-submitter@bugs.debian.org Message-ID: <5e1f44f6c9fc2_37682ab64d19cbc48283@godard.mail> References: <20200115150418.GH26536@debian> Subject: Bug#948980: marked as pending in schleuder Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Virus-Scanned: amavisd-new at glei.ch X-Loop: owner@bugs.debian.org Original-Sender: noreply@salsa.debian.org X-Loop: owner@bugs.debian.org X-Debian-PR-Message: report 948980 X-Debian-PR-Package: schleuder X-Debian-PR-Keywords: X-Debian-PR-Source: schleuder X-Spam-Bayes: score:0.0000 Tokens: new, 16; hammy, 107; neutral, 24; spammy, 6. spammytokens:0.999-1--H*RT:sk:noreply, 0.993-1--HAuto-Submitted:auto-generated, 0.958-+--H*r:sk:noreply, 0.950-+--H*F:U*noreply, 0.919-+--H*rp:U*noreply hammytokens:0.000-+--sk:salsa.d, 0.000-+--sk:salsad, 0.000-+--UD:salsa.debian.org, 0.000-+--salsadebianorg, 0.000-+--salsa.debian.org Auto-Submitted: auto-generated Delivered-To: schleuder@packages.qa.debian.org Delivered-To: dispatch+schleuder@tracker.debian.org X-Loop: dispatch@tracker.debian.org X-Distro-Tracker-Keyword: bts X-Distro-Tracker-Package: schleuder List-Id: X-Debian: tracker.debian.org X-Debian-Package: schleuder X-PTS-Package: schleuder X-PTS-Keyword: bts Precedence: list List-Unsubscribe: Control: tag -1 pending Hello, Bug #948980 in schleuder reported by you has been fixed in the Git repository and is awaiting an upload. You can see the commit message below and you can check the diff of the fix at: https://salsa.debian.org/ruby-team/schleuder/commit/d88717ad33d773736929d9d3ecb0eaace0779c1f ------------------------------------------------------------------------ debian/patches: Add patch to add List-Id header to admin notifications Closes: #948980 ------------------------------------------------------------------------ (this message was generated automatically) -- Greetings https://bugs.debian.org/948980 schleuder-5.0.1/spec/fixtures/mails/not_bounces/Bug_948981-_marked_as_pending_in_schleuder.eml000066400000000000000000000162371502127241700323300ustar00rootroot00000000000000Received: from immer7-21.glei.ch ([192.168.1.91]) by immer12-24.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) id 1irm4d-0006qL-Oz for team@schleuder.org; Wed, 15 Jan 2020 18:03:19 +0100 Received: from localhost ([127.0.0.1]) by immer7-21.glei.ch with esmtp (Exim 4.92.3) id 1irm4d-0008VP-4b for team@schleuder.org; Wed, 15 Jan 2020 18:03:19 +0100 Received: from immer7-21.glei.ch ([127.0.0.1]) by localhost (immer7-21.glei.ch [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 7tWjDBBn5KEd for ; Wed, 15 Jan 2020 18:03:14 +0100 Received: from muffat.debian.org ([209.87.16.33]) by immer7-21.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.3) id 1irm4Y-0008U4-Dr for team@schleuder.org; Wed, 15 Jan 2020 18:03:14 +0100 Received: from ticharich.debian.org ([2607:f8f0:614:1::1274:64]:48876)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=ticharich.debian.org,EMAIL=hostmaster@ticharich.debian.org (verified)=0D=0A by muffat.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4Q-0007Jl-Vt=0D=0A for team@schleuder.org; Wed, 15 Jan 2020 17:03:06 +0000 Received: from localhost ([::1]:49210 helo=ticharich.debian.org) by ticharich.debian.org with esmtp (Exim 4.92) (envelope-from ) id 1irm4Q-00088E-LN for team@schleuder.org; Wed, 15 Jan 2020 17:03:06 +0000 Received: from muffat.debian.org ([2607:f8f0:614:1::1274:33]:58328)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=muffat.debian.org,EMAIL=hostmaster@muffat.debian.org (verified)=0D=0A by ticharich.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4Q-000887-H3=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:06 +0000 Received: from quantz.debian.org ([2607:f8f0:614:1::1274:73]:57878)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=quantz.debian.org,EMAIL=hostmaster@quantz.debian.org (verified)=0D=0A by muffat.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4Q-0007JW-8K=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:06 +0000 Received: from qa by quantz.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4P-0002Bn-Oq for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:05 +0000 Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39]:51054)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=buxtehude.debian.org,EMAIL=hostmaster@buxtehude.debian.org (verified)=0D=0A by quantz.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4P-0002BJ-Mx=0D=0A for schleuder@packages.qa.debian.org; Wed, 15 Jan 2020 17:03:05 +0000 Received: from debbugs by buxtehude.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4P-0007ab-Fn; Wed, 15 Jan 2020 17:03:05 +0000 Received: via spool by 948981-submitter@bugs.debian.org id=U948981.157910757828002 (code U ref 948981); Wed, 15 Jan 2020 17:03:04 +0000 Received: (at 948981-submitter) by bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72]:55082)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)=0D=0A by buxtehude.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm14-0007HC-1q=0D=0A for 948981-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from godard.debian.org ([209.87.16.44]:52230)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=clientcerts/godard.debian.org,EMAIL=hostmaster@clientcerts/godard.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm12-0000MQ-BS=0D=0A for 948981-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:36 +0000 Received: by godard.debian.org (Postfix, from userid 3510) id DA71BA0D9A; Wed, 15 Jan 2020 16:59:34 +0000 Resent-Date: Wed, 15 Jan 2020 17:03:04 +0000 Resent-From: Debian BTS Resent-To: Georg Faerber Resent-Message-ID: Date: Wed, 15 Jan 2020 16:59:34 +0000 From: Georg Faerber Sender: noreply@salsa.debian.org Reply-To: Georg Faerber , 948981-quiet@bugs.debian.org To: 948981-submitter@bugs.debian.org Message-ID: <5e1f44f6ccc01_37683fd3c001e4f0829f3@godard.mail> References: <20200115150937.GI26536@debian> Subject: Bug#948981: marked as pending in schleuder Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Virus-Scanned: amavisd-new at glei.ch X-Loop: owner@bugs.debian.org Original-Sender: noreply@salsa.debian.org X-Loop: owner@bugs.debian.org X-Debian-PR-Message: report 948981 X-Debian-PR-Package: schleuder X-Debian-PR-Keywords: fixed-upstream X-Debian-PR-Source: schleuder X-Spam-Bayes: score:0.0000 Tokens: new, 16; hammy, 122; neutral, 34; spammy, 6. spammytokens:0.999-1--H*RT:sk:noreply, 0.993-1--HAuto-Submitted:auto-generated, 0.958-+--H*r:sk:noreply, 0.950-+--H*F:U*noreply, 0.919-+--H*rp:U*noreply hammytokens:0.000-+--sk:salsa.d, 0.000-+--sk:salsad, 0.000-+--UD:salsa.debian.org, 0.000-+--salsadebianorg, 0.000-+--salsa.debian.org Auto-Submitted: auto-generated Delivered-To: schleuder@packages.qa.debian.org Delivered-To: dispatch+schleuder@tracker.debian.org X-Loop: dispatch@tracker.debian.org X-Distro-Tracker-Keyword: bts X-Distro-Tracker-Package: schleuder List-Id: X-Debian: tracker.debian.org X-Debian-Package: schleuder X-PTS-Package: schleuder X-PTS-Keyword: bts Precedence: list List-Unsubscribe: Control: tag -1 pending Hello, Bug #948981 in schleuder reported by you has been fixed in the Git repository and is awaiting an upload. You can see the commit message below and you can check the diff of the fix at: https://salsa.debian.org/ruby-team/schleuder/commit/aa44bf0766a5b6a38d3784048a7d20a6567c0f75 ------------------------------------------------------------------------ debian/patches: Add patch to handle decryption problems gracefully Handle incoming mails encrypted to an absent key, using symmetric encryption or containing PGP-garbage in a more graceful manner: Don't throw an exception, don't notify (and annoy) the admins, instead inform the sender of the mail how to do better. Closes: #948981 ------------------------------------------------------------------------ (this message was generated automatically) -- Greetings https://bugs.debian.org/948981 schleuder-5.0.1/spec/fixtures/mails/not_bounces/Bug_948982-_marked_as_pending_in_schleuder.eml000066400000000000000000000157731502127241700323350ustar00rootroot00000000000000Received: from immer7-21.glei.ch ([192.168.1.91]) by immer12-24.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92) id 1irm4f-0006s8-Nx for team@schleuder.org; Wed, 15 Jan 2020 18:03:21 +0100 Received: from localhost ([127.0.0.1]) by immer7-21.glei.ch with esmtp (Exim 4.92.3) id 1irm4f-0008Vj-Ed for team@schleuder.org; Wed, 15 Jan 2020 18:03:21 +0100 Received: from immer7-21.glei.ch ([127.0.0.1]) by localhost (immer7-21.glei.ch [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 09bs5tCPPwGX for ; Wed, 15 Jan 2020 18:03:17 +0100 Received: from mailly.debian.org ([82.195.75.114]) by immer7-21.glei.ch with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.92.3) id 1irm4a-0008US-Rr for team@schleuder.org; Wed, 15 Jan 2020 18:03:16 +0100 Received: from ticharich.debian.org ([2607:f8f0:614:1::1274:64]:40220)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=ticharich.debian.org,EMAIL=hostmaster@ticharich.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4V-0000X2-LU=0D=0A for team@schleuder.org; Wed, 15 Jan 2020 17:03:11 +0000 Received: from localhost ([::1]:49340 helo=ticharich.debian.org) by ticharich.debian.org with esmtp (Exim 4.92) (envelope-from ) id 1irm4U-0008B7-AG for team@schleuder.org; Wed, 15 Jan 2020 17:03:10 +0000 Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72]:51378)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)=0D=0A by ticharich.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4U-0008A5-4f=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:10 +0000 Received: from quantz.debian.org ([2607:f8f0:614:1::1274:73]:52758)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=quantz.debian.org,EMAIL=hostmaster@quantz.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4S-0000Uj-Ki=0D=0A for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:08 +0000 Received: from qa by quantz.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4R-0002FA-Ak for dispatch+schleuder@tracker.debian.org; Wed, 15 Jan 2020 17:03:07 +0000 Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39]:51074)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=buxtehude.debian.org,EMAIL=hostmaster@buxtehude.debian.org (verified)=0D=0A by quantz.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm4R-0002Ee-8i=0D=0A for schleuder@packages.qa.debian.org; Wed, 15 Jan 2020 17:03:07 +0000 Received: from debbugs by buxtehude.debian.org with local (Exim 4.92) (envelope-from ) id 1irm4R-0007bJ-1Z; Wed, 15 Jan 2020 17:03:07 +0000 Received: via spool by 948982-submitter@bugs.debian.org id=U948982.157910757828013 (code U ref 948982); Wed, 15 Jan 2020 17:03:06 +0000 Received: (at 948982-submitter) by bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72]:55086)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)=0D=0A by buxtehude.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm14-0007HF-8g=0D=0A for 948982-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:38 +0000 Received: from godard.debian.org ([209.87.16.44]:52234)=0D=0A from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=clientcerts/godard.debian.org,EMAIL=hostmaster@clientcerts/godard.debian.org (verified)=0D=0A by mailly.debian.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256)=0D=0A (Exim 4.92)=0D=0A (envelope-from )=0D=0A id 1irm12-0000MV-NP=0D=0A for 948982-submitter@bugs.debian.org; Wed, 15 Jan 2020 16:59:36 +0000 Received: by godard.debian.org (Postfix, from userid 3510) id 44B93A1219; Wed, 15 Jan 2020 16:59:35 +0000 Resent-Date: Wed, 15 Jan 2020 17:03:06 +0000 Resent-From: Debian BTS Resent-To: Georg Faerber Resent-Message-ID: Date: Wed, 15 Jan 2020 16:59:35 +0000 From: Georg Faerber Sender: noreply@salsa.debian.org Reply-To: Georg Faerber , 948982-quiet@bugs.debian.org To: 948982-submitter@bugs.debian.org Message-ID: <5e1f44f73e3af_37683fd3c001e4f083093@godard.mail> References: <20200115151452.GJ26536@debian> Subject: Bug#948982: marked as pending in schleuder Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Virus-Scanned: amavisd-new at glei.ch X-Loop: owner@bugs.debian.org Original-Sender: noreply@salsa.debian.org X-Loop: owner@bugs.debian.org X-Debian-PR-Message: report 948982 X-Debian-PR-Package: schleuder X-Debian-PR-Keywords: fixed-upstream X-Debian-PR-Source: schleuder X-Spam-Bayes: score:0.0000 Tokens: new, 15; hammy, 113; neutral, 25; spammy, 6. spammytokens:0.999-1--H*RT:sk:noreply, 0.993-1--HAuto-Submitted:auto-generated, 0.958-+--H*r:sk:noreply, 0.950-+--H*F:U*noreply, 0.919-+--H*rp:U*noreply hammytokens:0.000-+--sk:salsad, 0.000-+--sk:salsa.d, 0.000-+--UD:salsa.debian.org, 0.000-+--salsadebianorg, 0.000-+--salsa.debian.org Auto-Submitted: auto-generated Delivered-To: schleuder@packages.qa.debian.org Delivered-To: dispatch+schleuder@tracker.debian.org X-Loop: dispatch@tracker.debian.org X-Distro-Tracker-Keyword: bts X-Distro-Tracker-Package: schleuder List-Id: X-Debian: tracker.debian.org X-Debian-Package: schleuder X-PTS-Package: schleuder X-PTS-Keyword: bts Precedence: list List-Unsubscribe: Control: tag -1 pending Hello, Bug #948982 in schleuder reported by you has been fixed in the Git repository and is awaiting an upload. You can see the commit message below and you can check the diff of the fix at: https://salsa.debian.org/ruby-team/schleuder/commit/7418064a3c09ce049a833230d8284fb7243c334e ------------------------------------------------------------------------ debian/patches: Add patch to default to ASCII-8BIT encoding This should ensure Schleuder is able to handle mails with different charsets. Closes: #948982 ------------------------------------------------------------------------ (this message was generated automatically) -- Greetings https://bugs.debian.org/948982 schleuder-5.0.1/spec/fixtures/mails/not_bounces/tt_1234210666.txt.eml000066400000000000000000000037251502127241700250330ustar00rootroot00000000000000From agris.ameriks@accenture.com Mon Feb 9 22:17:46 2009 Return-Path: X-Original-To: ameriks@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from ememr1003.accenture.com (ememr1003.accenture.com [170.252.72.95]) by ameriks (Postfix) with ESMTPS id 237A53A08C for ; Mon, 9 Feb 2009 22:17:46 +0200 (EET) Received: from EMEXV1004.dir.svc.accenture.com (emexv1004.dir.svc.accenture.com [10.130.16.107]) by ememr1003.accenture.com (8.13.8/8.13.8) with ESMTP id n19KHQQp012745 for ; Mon, 9 Feb 2009 20:17:43 GMT Received: from emexr1003.dir.svc.accenture.com ([10.130.16.112]) by EMEXV1004.dir.svc.accenture.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 9 Feb 2009 21:16:34 +0100 Received: from EMEXM0302.dir.svc.accenture.com ([10.130.16.127]) by emexr1003.dir.svc.accenture.com with Microsoft SMTPSVC(6.0.3790.1830); Mon, 9 Feb 2009 21:16:38 +0100 X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.3168 Content-class: urn:content-classes:message MIME-Version: 1.0 Importance: normal Priority: normal Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Subject: Arpus biroja atrasanas automatiska atbilde: subjecting Date: Mon, 9 Feb 2009 21:16:32 +0100 Message-ID: X-MS-Has-Attach: X-MS-TNEF-Correlator: Thread-Topic: subjecting Thread-Index: AcmK80zS8L6DgfKxTMON80Obs/ge4QAAAAOa From: To: X-OriginalArrivalTime: 09 Feb 2009 20:16:38.0386 (UTC) FILETIME=[50313D20:01C98AF3] Thank you for your email. I am out of office and will be back on Tuesday 9:00. This message is for the designated recipient only and may contain = privileged, proprietary, or otherwise private information. If you have = received it in error, please notify the sender immediately and delete = the original. Any other use of the email by you is prohibited. schleuder-5.0.1/spec/fixtures/mails/not_bounces/tt_1234211024.txt.eml000066400000000000000000000042301502127241700250100ustar00rootroot00000000000000From MAILER-DAEMON Mon Feb 9 22:23:44 2009 Return-Path: <> X-Original-To: bounces-main+agris.ameriks=gmail.com@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from mail-bw0-f164.google.com (mail-bw0-f164.google.com [209.85.218.164]) by ameriks (Postfix) with ESMTP id 1F0A13A08C for ; Mon, 9 Feb 2009 22:23:44 +0200 (EET) Received: by bwz8 with SMTP id 8so2417119bwz.4 for ; Mon, 09 Feb 2009 12:23:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:message-id:date:from:to:subject :mime-version:content-type:content-transfer-encoding :content-disposition:precedence:x-autoreply; bh=N1fY4J/R56Y76WNR/I9bQfBBxJb49OKIxsGFWNKUWVY=; b=drTotfyYsI7Vhh+BvIhvVQY8uI3KoDNsYyidJ74rEYVsC/AizeWH6KkNdzbtyn4DXl d9qm2tzYGTZk7oRA1IDQWG8iGFfJKtz5d9JPLD9StGsQhZ+mTne0m32hqsMKxcG+1TE7 DcqUJJ7lqMsoWIm9/qfrKh4M9WpYg+EsR1np0= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=message-id:date:from:to:subject:mime-version:content-type :content-transfer-encoding:content-disposition:precedence :x-autoreply; b=Tq6LsLhors0+QfBe4baI1UEuuMm2RwJVE1WJS6i2qaPn557zNbnRYQfrv9m2hb93Ks /2GIjkIa+GT0QN9WIY9qCywiojBtznRT8hjk5C3OibuZdGMjCQn1l2lA8xgEG8jcdMGR Cir8us4vyOqUQjhQ6Vl/m/6FwENpgU8/H1YIk= Received: by 10.181.218.14 with SMTP id v14mr357446bkq.48.1234211023205; Mon, 09 Feb 2009 12:23:43 -0800 (PST) Message-ID: Date: Mon, 9 Feb 2009 12:23:43 -0800 From: "Agris Ameriks" To: bounces-main+agris.ameriks=gmail.com@amerimail.lv Subject: =?ISO-8859-13?Q?Atvalin=E2jums_Re:_subjecting?= MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-13 Content-Transfer-Encoding: base64 Content-Disposition: inline Precedence: bulk X-Autoreply: yes RXNtdSBhdHZh72lu4mp1beIKCi0tIAotLS0tLS0tLS0tCldpdGggcmVnYXJkLApBZ3JpcyBBbWVy aWtzClBob25lICszNzEgMiA2NDYxMTAxCkUtbWFpbDogQWdyaXMuQW1lcmlrc0BnbWFpbC5jb20K schleuder-5.0.1/spec/fixtures/mails/not_bounces/tt_1234241664.txt.eml000066400000000000000000000020741502127241700250310ustar00rootroot00000000000000From bounces-main+bratchka=amerimail.lv@amerimail.lv Tue Feb 10 06:54:24 2009 Return-Path: X-Original-To: bratchka@amerimail.lv Delivered-To: bounce_and_mail_handler@ameriks Received: from Albanis-3.local (unknown [87.99.72.11]) by ameriks (Postfix) with ESMTP id 3499D3A08C for ; Tue, 10 Feb 2009 06:54:24 +0200 (EET) Received: by Albanis-3.local (Postfix, from userid 501) id D9FB832925D; Tue, 10 Feb 2009 06:54:22 +0200 (EET) Date: Tue, 10 Feb 2009 06:54:14 +0200 From: ameriks@amerimail.lv Reply-To: ginta.amerika@ameri.lv To: bratchka@amerimail.lv Subject: Mes loti atvainojamies, ja sanemat so epastu Mime-Version: 1.0 Content-Type: text/html; charset=utf-8 Message-Id: <20090210045423.D9FB832925D@Albanis-3.local> Labdien, mes loti atvainojamies, ja sanemat so epastu. Mes paslaik veicam jaunas sistemas izstradi, kur ir arii ieklauta epastu sutisana. Ludzu ignorejiet so e-pastu! Paldies par sapratni. Ja jus velaties ar mums sazinaties si epasta sakaraa, sutiet epastu uz agris.ameriks@ameri.lv. schleuder-5.0.1/spec/fixtures/mails/plain/000077500000000000000000000000001502127241700205075ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/plain/thunderbird.eml000066400000000000000000000055771502127241700235360ustar00rootroot00000000000000Return-Path: Delivered-To: n.siessegger@zeromail.org Received: from smtpin.nadir.org (smtpin.nadir.org [217.114.68.218]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by snail.zeromail.org (Postfix) with ESMTPS id D7C8EC001D for ; Tue, 27 Dec 2016 17:18:59 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by smtpin.nadir.org (Postfix) with ESMTP id AB2BDE60C1 for ; Tue, 27 Dec 2016 17:18:59 +0100 (CET) Received: from smtpin.nadir.org ([127.0.0.1]) by localhost (smtpin-na.nadir.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id GbNqpozJuesw for ; Tue, 27 Dec 2016 17:18:57 +0100 (CET) Received: from mout.web.de (mout.web.de [212.227.15.4]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtpin.nadir.org (Postfix) with ESMTPS id A91EAE604F for ; Tue, 27 Dec 2016 17:18:57 +0100 (CET) Received: from nna.local ([80.187.107.60]) by smtp.web.de (mrweb004 [213.165.67.108]) with ESMTPSA (Nemesis) id 0McT8i-1c4NZw3xE8-00Hgam for ; Tue, 27 Dec 2016 17:18:57 +0100 To: n.siessegger@zeromail.org From: Nina Siessegger Subject: Test Message-ID: <8db04406-e2ab-fd06-d4c5-c19b5765c52b@web.de> Date: Tue, 27 Dec 2016 17:18:57 +0100 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit X-Provags-ID: V03:K0:q07lv8kyV5vZ2Bkz3y99DthdSlg2NU0SQbMMZtb6PkyKzuO3TqS O2/qixJf4THt+J1OEiMuWOG71sYXBENA7fUZzy25Xy3mpDLgOJj55qE8+KOFuLfWPyb2OeI 39NYZ0+mLzIuJEiq4JnfYnF7s1SN5jv3CadFuO206MV7recIxXOjpxHPDvoyfzLMyxqRb5b jM6SATIl6pRdymPUNFsjQ== X-UI-Out-Filterresults: notjunk:1;V01:K0:9gbmm2iZtw4=:psOpZ/aY5zv6agYRXPOnce fJ6S+HVb03oKnQICo4EZZxwCuxBpd1xUHzJFid+KUGgvVNXPwpi5Z0hg57WQQYwhopWhY2eCm o9nJ6xMVS8CV8uX0lEXsoNM/JRt5oM1OvIIQuuuAzUDw7F1nnmGQPOMleAMAQ7GyvfirSZfx4 VrLOsF0vIZJOwoP5rZusgqrCteOJn5TC+Wy5UDb0w3ogzJQRsNLTN2Q6f5b7mzuT1zEVPdpU2 HMHvoRTVQpMYtfTjoVJlVKfh5Zf6eurPDFa980+/h5xQKf0+W+u1Z1JCe8zI45628TPPzQNN9 VvugURsiPDP3h3zxj/joo6O2l4Vk/30Mxr7k+PVhSLno4GNjS5oL52o+AG0ZRjD3fKWrnArXr j0emey1AI1EZij+FwwWyf0JF7ubRvN2qXRovU+ZYZ3fV1x56S5ZXdiuOqImHzgYcCdKQ1Mbj3 HX+jleuL6Gkgv2hdHcq++zErqiSbBhJKxPfDGAIwRpb/kAf3/sEGGhdLAlzlagXbbNgScVNsa PQIRuXvDim+eTk8Wb0Wh17flIzpgtz/bU8ZA61WLN8yt1v3aGevuwbMbO75dO/QOU4s4Yaaln 2LUsTYwtLKuqOY+reg6J3dZYyRwMTIl8961GcGXI3xHyHlKORKPk36b0/H0kHRi337qNl5mKF Z4WiQdpohcsGg0plEuZRLdxJ7knXQX3kZxO2FlTAhfPdWN1MDuQ+CiWi8MRwApOTQeR0nzRuR 2RAcnGeDuEgC3L/e6jkVJTvg45eulIeiIQPtEmx7eC3xs+wnifTCXqI4Mi9SDWtQxnPmWPPrh QAox8QKjkrSq/JyqvmWV7fCOJur8Wmms+oyr8g/MpOBqTMGbZumCNBa4UaGnSDTFon8Qg8djv c4C7eaiiU7Z6bS9xU/qA== Test schleuder-5.0.1/spec/fixtures/mails/protected-headers-request.eml000066400000000000000000000061411502127241700251750ustar00rootroot00000000000000From - Fri Sep 29 10:33:00 2017 Subject: Encrypted Message From: paz To: schleuder-request@example.org Message-ID: Date: Fri, 29 Sep 2017 10:33:00 +0200 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 HW0yibfpQagojjK/7t51eJBeLYfJgA80 =yqIQ -----END PGP MESSAGE----- --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi-- schleuder-5.0.1/spec/fixtures/mails/protected-headers.eml000066400000000000000000000043021502127241700235040ustar00rootroot00000000000000From - Fri Sep 29 10:33:00 2017 Subject: Encrypted Message From: paz To: schleuder@example.org Message-ID: Date: Fri, 29 Sep 2017 10:33:00 +0200 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- hQIMA691X8Gl2MArAQ/9EkuJLshPrMOSe38BjyUBe+1L2hUQOxBd0MI0d/C0Yadj 7YtlwveeqTpLGuT7H+ecaVlhMC9KFL4NT8v64D2e4S68wymVHUqO8SezMW8Bp5ut 2psZb+/KqF7oKLkkOu6neKo8PODrDcPK5pQGBBdV9DP2Z7uNfSyTJvVfNsf/3Q7e mpQAgj9g2uooMPSqNRhOOrLPL6uWPIkfcvnt1gb4eG++ltpBD243yRxnUFrUxghq rVH6V1la0d/WO3sLOu3oVAC71NPNq5DilVFfKl+/H5BwK54pmmuvu05JKgZw1QqJ lImWRFv8GPmeyvqu9F0A4E0e4aFIly5/uHrBq0uw7R/gmeUICcwb28/ZftPj2ZEr O9C0flYVwWB3cor5owM0rOtcgbwBI6EvSQ1d51qNA1PnpIu7woptFl10tguBiJ5X HmrK+nOaXy1Rf/clrrmd/BeLtuXheb+N6WpWgiwV1MSZqP8IFBYq7R/STQpWyhxO qmMGx27iJmTHx1nQpfzZybiecws1oOHY/gOc7uI1I4+zfitdRBsOozQDB8K8lEjK 3lsDmQoCu8GUQJj6t6OGn5YlEQ+oSGNc189sDThgGG5KDVGqd5OoI0ckxWam1GeR b40imecyWm5v2aejOaHisK463YsUSO8HsIUDEtCllvl4gF1dxeUvflWKSNqqwrPS wS4BC3VdOj2RY6vxtTBpfWLqLqsMQ970wXV9oKCLSrbobgJSwUleU3I1WGhXegDI hBxRFvYIDr3R6OTDnbGTnB7We//mk5GN5eXAMs5o7Zqcn54Mox471x8Veg1dUoj2 2c6/Vgg5yX1cy+5SaZGSYDeKbyx2PSoMXZZWQHQ+KPe527gLAzpoeUAWN1H+lU6y eORjBBlk/GQF15r5BmytQraTqsJTRG22SpycpYNugzgJPzozdjXGZ5Vdz9UHo3FC Ch6gNXjeDXNCJ8PRQr3LQKrIsT6nE7zmF0N69LwJfMSm5MYIJnkhJ5tzJ/7MmcUr OfgVkorhjM2oz4w8imI+2xFy4LhOKYEr0JHBN8X5QZ5yYQ2CVPDX3MwvpW0qn5PH iQSC99Kabne9rmCByx7Ue+WUCZkuTJq201U0iSf2gNu2e8ebz+Y0ZxJ7JgH1eqyU MLfSxyFry3JQ84zHgIFJV6EPmk4W+JhsdoPGTYghsUYpBaGsn/e1JPhpn3+mQnPV +KH+x2ES9EmSMmHYBV5RY7YQB31hqnazY+omqSCgZotBq18/jHg2fnzEtvzj0fEr PgMyC/zUcSTWknToRhU7dVFzYs19/1vnJJthAQxH7Zbx5kfisYVMcyh2OSEpaPH/ C1d7wjZUT2goLpGaDYK+xQ== =u70c -----END PGP MESSAGE----- --NNqdAeeD0BMoMnLVbGvlJ93USlrgv66Hi-- schleuder-5.0.1/spec/fixtures/mails/qp-encoding-clear.eml000066400000000000000000000012401502127241700233700ustar00rootroot00000000000000Date: Tue, 11 Jul 2017 12:00:40 +0200 From: test@localhost To: schleuder@example.org Subject: Input Test QP encoding broken Message-ID: <20170711100040.GF7104@blabla> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On 17-07-11 11:59:56, schleuder@nadir.org wrote: > From: foo > To: schleuder@example.org > Cc:=20 > Date: Tue, 11 Jul 2017 11:59:49 +0200 > Sig: Good signature from somethingsomething > Enc: Encrypted > foo foo foo > foo foo foo > foo foo foo >=20 > [1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D867031 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D867031 schleuder-5.0.1/spec/fixtures/mails/qp-encoding-encrypted+signed.eml000066400000000000000000000061621502127241700255540ustar00rootroot00000000000000From: test@localhost Subject: something To: schleuder@example.org Message-ID: <381fb765-1e53-4578-bc93-bff7e689cbaa@example.org> Date: Fri, 21 Jul 2017 11:50:35 +0200 MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3 Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3 Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 Xe7gviDKl62tMcXuN8d5E0RUeVdCGEaSQQy7 =QAIj -----END PGP MESSAGE----- --OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3-- schleuder-5.0.1/spec/fixtures/mails/qp-encoding-encrypted.eml000066400000000000000000000034711502127241700243070ustar00rootroot00000000000000Date: Tue, 11 Jul 2017 12:00:40 +0200 From: test@localhost To: schleuder@example.org Subject: Input Test QP encoding broken encrypted Message-ID: <20170711100040.GF7104@blabla> MIME-Version: 1.0 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="4f28nU6agdXSinmL" Content-Disposition: inline --4f28nU6agdXSinmL Content-Type: application/pgp-encrypted Content-Disposition: attachment Version: 1 --4f28nU6agdXSinmL Content-Type: application/octet-stream Content-Disposition: attachment; filename="msg.asc" -----BEGIN PGP MESSAGE----- hQIMA691X8Gl2MArARAAreFTl/Yz+V0YBDfl54Zz+S3rENPDKXEmNJNzcYoLIEuH beoeWMHY9nzD7QkaNaeNL3foK9gr62njvrf6KsQVgnoUE8cQFhk2FJkPoylWGAnT 2zI/dZLpMit/J3WSG1U02C/lOXN7M1kuvx7O/tGiKRtudyCwGO+qrAp/BCEIwBDz sJcEo9HAU4odV1oPFGDDw+y+EfOmTj+afplsSft0hDmI+hWnkjaWKCxhmR5BEWr2 fBkaAzH4rMls8g22llZrL3Rens4UT/htOMiDXoxBQ4oE3P+sb8WesmpSJwU77y3J 7DBdwBfI0q4kI3EjOBMvOQPLCYZl5+3LckQRl5QGPaBC+BaeAsXS2dEPRl5Xw1Ae ZzxTgusV7V0hDhNG3z3SzmczfUZ7+6OQ7HsOhHAVa3KmuWpaw8Dr5s2n6AhzZNGs A8HmWQETa9DxGQTKrcijB5LyIFTo8d5Hc144En9demO0ApBd3Jg5lhkAr+vAL/z0 OkL35vosRWMncHx2GB6ido7IoMz4zXiKW34ezXJWvH5SwRdjOuy6WMvOzGToxJcn fK55f/xoq63N/uhHuVy8BH56AjI5OAfEowZ1jb/pGGRGM36JwrdwInbwSMU/X34r 6ERESKrDqRvk27u0C10iAMFMfIdQVlfJN+kKXxyKDxOaukbSexnaFBNyi07b7DzS wLUBqDDjF8zXjCQNvOlqcBrVht3e0H4YqUFPfXy+fuGYL6FWobjuUDN9ZYbcuDwu w6ioBRoFmUwvOzTI9S40+JDR8RcFPbZuYYzBEZnqNJapsy7XrvXQUXlmOB7klm0s 4m/d5edXlp93KqbO+oJ05Bse88cXvfDIrRKkV+YSWOB9VxAES3SK79QQRYlJlEdn zzcmSt8ODewc6WfO93u0gG/2xCftEHpi/iHFlgrzSVuZMa0Vn5AXva819aJ3t9D7 OYKEp2IARQHSB30W7iIOK/cyS6ymE27137uETNQ7FinES9929E9WCd+3vk/lSZLr DnhSuGSNRV36srVsN7SkbbrvfF6iipJDC3Y1GQVmngpu6nU1r0/koqGkeIJr7Sao mGIgpVBBZYbhS3LBw6h87gtYEbKirwNLuP+WHY2uQkLIBCv7/ugrATHn503nwwEp GNFu+Bhh6FQZhMfgIlSHpV5jsHeMQFqy/xgW5VgnWhqZAEEKW1AA =IB9S -----END PGP MESSAGE----- --4f28nU6agdXSinmL-- schleuder-5.0.1/spec/fixtures/mails/signed-inline/000077500000000000000000000000001502127241700221315ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/signed-inline/thunderbird.eml000066400000000000000000000027501502127241700251460ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id 777D3801DD for ; Thu, 29 Dec 2016 15:21:42 +0100 (CET) To: schleuder@example.org From: paz Subject: signed-inline Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: Date: Thu, 29 Dec 2016 15:21:42 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 This is a test. Umlauts: öäß. ♥★ -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEUlB7AWOo2fAJT/4DsaNvCAaeVd4FAlhlG/YACgkQsaNvCAae Vd4g5A/+Joc1sst26EGHhYL/MrYICBljcvFHWYHo81sr6ED1ek18eWiHtHGFhWvO HyJ0Ccu1ktdcyay7K70gpRNrjvs2d3l5sAAfgMyDPsvBrvWHKYUpiypcVG/7FRrD 1RVsQroh4xTo2wzWOjn47dT0EMhVZtg4jITSQn25dj8/D9viUeE3fiGFLtG5KLhU B1l+3A5cYrzaSRy9p6VoCPSEOCToZ34LJd9RkG/QKMD9JT/HcZN89sRVL0e4qI8e wQWsI6Oi7WNGiUA+p8Q5un90KNCqqumjkVxIiiFk5wSB4egba6JIBW43pKYrklRz wIXrNCk/RWcPJMTqIH9n+2IrDlKeYO/u+h/uh1y0SOTnImkmpfcgYsG3ggPnVask T2J8CCQ5Xy0HecyEFt5GO9pYG73vG9bX/YQ3cGN6yUJxmXzlQ02iS6UVx5g8Ypy2 NuZwQKFUGUeFta2mRlkKn8VZfqXrWv3VtKoaa1Zm0gZydTDA/37W5A2xrGk9IvjZ CUGx4jLqXbOFJ+zOthI9JJmaPP4sc7hUmegAjV1PJP6AaIZ+6TLwn+gY1z7SaRd2 59bA6Bnm4S7p8nV7oPwW/WaOUu3mF7y5w4Yd8DAS5BKC5UiBexrhY6mH71jXb+J9 VZOIwSEI9QvBKQM/7wULApVGq1kPR3K6XKAMvWiez3ayqsYUflk= =TSUJ -----END PGP SIGNATURE----- schleuder-5.0.1/spec/fixtures/mails/signed-mime/000077500000000000000000000000001502127241700216025ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/mails/signed-mime/thunderbird.eml000066400000000000000000000043651502127241700246230ustar00rootroot00000000000000Received: from [IPv6:::1] (localhost.localdomain [IPv6:::1]) by flutsch.local (Postfix) with ESMTP id E276680329 for ; Thu, 29 Dec 2016 15:14:42 +0100 (CET) To: schleuder@example.org From: paz Subject: signed-mime test Openpgp: id=52507B0163A8D9F0094FFE03B1A36F08069E55DE Message-ID: Date: Thu, 29 Dec 2016 15:14:42 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1 MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="DnUG3PvpPDcqa5kwpXHvXR1RjOgf17M3b" This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --DnUG3PvpPDcqa5kwpXHvXR1RjOgf17M3b Content-Type: multipart/mixed; boundary="L98FCUdRgUPMMRg5CUqPXDjjv0kGAXPf0"; protected-headers="v1" From: paz To: schleuder@example.org Message-ID: Subject: signed-mime test --L98FCUdRgUPMMRg5CUqPXDjjv0kGAXPf0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable This is a test Umlauts: =C3=B6=C3=A4=C3=9F =E2=99=A5=E2=98=85 --L98FCUdRgUPMMRg5CUqPXDjjv0kGAXPf0-- --DnUG3PvpPDcqa5kwpXHvXR1RjOgf17M3b Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEUlB7AWOo2fAJT/4DsaNvCAaeVd4FAlhlGlIACgkQsaNvCAae Vd5VQQ/7BfL7kQA30QfQMoOLqnww/jX2aCeOIAisuPE0CU1oqxn00ntSIl7vMYIl Gu16gfwXrftbNxLfEwlLN6xZbzPJg+JefumOZglAqP9zHONxuwhO3QkaN0+K2Gfl ECDVAOxVZX52APoFkkgJjkUcHmCa2GeFROhgmDtCxeGjBTQlRg6WNu+xeJmR9GUG MQSaaMxHHSFxTmXQonRa5C1ATw9ZXekDOHK8LEO5hq04WEus0zio0a6fi+/xgTUS rJb+9el+pSOa9pZ5ZFfWWqFYuG5d4GNQpNeZQZjnUKKt0PvSw8g6Sqe0UvAeyjyg HRjDAsCijMaMql4+w6Ko7T8vl/C2By0bBeJfpZ1YkR+wa28+v2u3G8l2Mk7azkzT a8QHMzyTITv3D3MunpXSrp6Blh9X2u5PmkD9BkxHduuz/J7kgvufini8oqc3WP4U zZ8ZfU3ruon2le4UCRE/kWtDgCEK8+ppYqF/GSJZ6j1lO+tOfuWjwhotdtdG88/x bRS2GXecp7Jy8aosC0c0MHqZEMRUCKfN9lQLFf3oi+7f1AhDZbTzVnI+KGduZU3h /gk3kwLHFUd2epIjiWOygQyfpMQCqNvaskz8md3uH9P2F1mfHi1CaUB5Nq1gLegP GNmgErDQLDCoBnEW3aNsEM/BzPy2JRMi9k/sPXQ0GsTZhH62MV0= =CSzt -----END PGP SIGNATURE----- --DnUG3PvpPDcqa5kwpXHvXR1RjOgf17M3b-- schleuder-5.0.1/spec/fixtures/mails/thunderbird-115.2.3-macos-xaddkey-attachment.eml000066400000000000000000000212371502127241700301040ustar00rootroot00000000000000Message-ID: <114ec1bc-0930-4501-94c0-a379cbf85101@schleuder.org> Date: Tue, 10 Oct 2023 15:00:09 +0200 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird From: "paz@schleuder.org" Subject: xak7 To: schleuder@example.org Content-Language: en-US Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="------------45oMbxrqyacC32ZCjsBuwZE1" This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156) --------------45oMbxrqyacC32ZCjsBuwZE1 Content-Type: application/pgp-encrypted Content-Description: PGP/MIME version identification Version: 1 --------------45oMbxrqyacC32ZCjsBuwZE1 Content-Type: application/octet-stream; name="encrypted.asc" Content-Description: OpenPGP encrypted message Content-Disposition: inline; filename="encrypted.asc" -----BEGIN PGP MESSAGE----- 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 0l54bNQDMlk7ZuK627Ko641ARyyUeo4= =fQ/g -----END PGP MESSAGE----- --------------45oMbxrqyacC32ZCjsBuwZE1-- schleuder-5.0.1/spec/fixtures/more_filters/000077500000000000000000000000001502127241700207715ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/more_filters/pre_decryption/000077500000000000000000000000001502127241700240175ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/more_filters/pre_decryption/166_late_example.rb000066400000000000000000000001111502127241700273710ustar00rootroot00000000000000module Schleuder::Filters def self.late_example(list, mail) end end schleuder-5.0.1/spec/fixtures/more_filters/pre_decryption/25_example.rb000066400000000000000000000001041502127241700263000ustar00rootroot00000000000000module Schleuder::Filters def self.example(list, mail) end end schleuder-5.0.1/spec/fixtures/more_filters/pre_decryption/6_early_example.rb000066400000000000000000000001121502127241700274120ustar00rootroot00000000000000module Schleuder::Filters def self.early_example(list, mail) end end schleuder-5.0.1/spec/fixtures/olduid_key.txt000066400000000000000000000033051502127241700211710ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFjWeYkBCADfzR16dDh1o+kYN1OzxDrKw+/DYvxpwggfgZ0nAZkdwM+oHpLB lRfKgfs1lghuMpff/DbTM0xiFUtqPACDrpf9sQ1VS0Z2zCfERy0SITZisBVAtwmg 551MSCBY/g2PWy455r20hcSJSuFLt+Dw8u/y5WNeDpnA7seXvPXs7JrXwwJEi/Ql XZK+1d9AgzQAlFhL2fvvOrAV4NM6JvMy7kpRScTt+szMiGLf+kfiYYlj58xdkhPJ AqU9g0qDnp6Ia7sgaU0Hgs4IWymM6sLrOjtQJI+2/MOx++FK0SpfLW84q++ANw6F dHmYAcXZ3C1iL6YZzkKTLJhwdpupekLxBdynABEBAAG0GW9sZCB1aWQgPG9sZEBl eGFtcGxlLm9yZz6JAU4EEwEKADgCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AW IQRu5R14/Qsz3mXM9p0hBOIOIIifZgUCWNZ5vwAKCRAhBOIOIIifZj5DCACizhev n/X/oloddPeYfAElO+KBf2Dx9XYRuEoJNb+w5pkwAbcvOKoSVQGPSwUw1IFHr7w4 WjfNRzDm8iBqPUc6jBEwAH9s+LN0rIcr2rDYtXkMZpCgfczPmVoDmMoFn7abnNZc XLMqtk3raRaWeYfzggveyGkUAdPx34N0R6MykZtIQaqnTxel1Htj/XeERfNwmIhX CrBaKsVWYncllPzulupv6xe1hBHlENXc2XEoUqphxJtQEkDGwPPXe5Rb/K7XyJh6 uuXgFWL5cT8OxYt6O8XRZIhdT80kLqLf9WkHQYBHYvMlstzcQYG33f16NIMuj1Ra ern2KdEzIwBCyMsxuQENBFjWeYkBCAD4HsM1gZ8UPtEOFWaUrFnEJLopS+B1iTBS ISdXV0tjQEnplr8Os8LPd2Y6niLYRt7+r0E2CFrZvZ6D9ZVnvK4ex10MnmEqLfgW eK6rssUONptCA46AzCYyGKLXjPzl2R+jtEfJIw3PugN8rSKx4ZFA1h6sPP7NCve6 taICeFtPB4NYT9WLdegoBCVMaRNBqatZv9t11Oq6j874H41ppQnyZ69C3PygGHUe rwx6sREis74QFHVuW10sm44buuGBnAV1vmiYK15s/ByZpcZdIaTljF31vZtIEJbb 9tKDvuSWKLlji3Ao72hn3bWdfOoZPenB8b0VPdteUAgDg4aXxeIjABEBAAGJATYE GAEKACACGwwWIQRu5R14/Qsz3mXM9p0hBOIOIIifZgUCWNZ50gAKCRAhBOIOIIif Zk1JCADbemG0mChBv3TVWZAMFuoqK7vXbDZ3bsJyxUFNeDs49BYO/h3iBbT8nCny bnCzIP0EcCMaZr5629b50fblQhEgndzvgvDOv3RfxUNcbw2SunpS4u2Q3c8TD4ps av1LzfzV+9ha2m1vNJHsKzcoWALNFz2a9KcKuvjvY/8NalsueVbMKgSjVaSLwvEj 9HldIjeeHy2OFMqIjk+DomdVrmcJ4J3ERGrIrq32O7zwWM2iDwx+EXMI2XEvqbbr WfHgGnmNBGJJC4Rs+kQB+PsMeaANXVLvdnX4aC/OAfZBB1UCxI2UFtKtxp3B+eJN TL561iNgOgEG8UAfKyReEwOUM81S =LUmS -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/olduid_key_with_newuid.txt000066400000000000000000000042651502127241700236050ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 Ug== =8V6x -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgp-keys/000077500000000000000000000000001502127241700207205ustar00rootroot00000000000000FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A-abcde_example_org.pub000066400000000000000000000012141502127241700316340ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/openpgp-keys-----BEGIN PGP PUBLIC KEY BLOCK----- mDMEZVYyWxYJKwYBBAHaRw8BAQdAkuQXZOMbeh0mx7k7aJ0fjzIuTJVG349hVpM/ ve3ruDW0GWFiY2RlIDxhYmNkZUBleGFtcGxlLm9yZz6ImQQTFgoAQRYhBPsYrikv zuvOO7P7oUKG7ldLkvoKBQJlVjJbAhsDBQkFo5qABQsJCAcCAiICBhUKCQgLAgQW AgMBAh4HAheAAAoJEEKG7ldLkvoKYbkBAPgyCzGOaF/y3X4qb6Zq3fzscHbGYNqd grSSrxpyvjM+AP0Xx10SLJ3YC6pJxfT6Zlh33WuaXzILizQUweIHfb26Dbg4BGVW MlsSCisGAQQBl1UBBQEBB0AciUwVVTIjOx3VEn2LmV/I/jaqO4Os5AE8wSNl+MND UAMBCAeIfgQYFgoAJhYhBPsYrikvzuvOO7P7oUKG7ldLkvoKBQJlVjJbAhsMBQkF o5qAAAoJEEKG7ldLkvoKDkwA/jHAE2eJV+YGUkwnouJKwYXvJdAzWeieVhCKTESM TxvzAP47F5KBuu6V7g3HGhNJopPGt1aERj+XQJ4fhIw8YZT1Ag== =9ogK -----END PGP PUBLIC KEY BLOCK----- FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A-abcde_example_org.sec000066400000000000000000000013401502127241700316200ustar00rootroot00000000000000schleuder-5.0.1/spec/fixtures/openpgp-keys-----BEGIN PGP PRIVATE KEY BLOCK----- lFgEZVYyWxYJKwYBBAHaRw8BAQdAkuQXZOMbeh0mx7k7aJ0fjzIuTJVG349hVpM/ ve3ruDUAAP445v2DH6WzCyjF2w0Vrk5eLE6hAkhHxYFjjhaqIlnvRw62tBlhYmNk ZSA8YWJjZGVAZXhhbXBsZS5vcmc+iJMEExYKADsCGwMFCwkIBwICIgIGFQoJCAsC BBYCAwECHgcCF4AWIQT7GK4pL87rzjuz+6FChu5XS5L6CgUCaEM2KgAKCRBChu5X S5L6CvVTAP9xpbQkGrVl1jmuAYbXlM8dWady7aaE+PvrsWPHZbzAEAEAzAVRrI9s wIvq0+Ke2kRLXugxIXtayeA9D0PXahDT7QOcXQRlVjJbEgorBgEEAZdVAQUBAQdA HIlMFVUyIzsd1RJ9i5lfyP42qjuDrOQBPMEjZfjDQ1ADAQgHAAD/aHa823HpvKcy rPcAqMxYBGRqYo/YsMGrb5hfWWeMhQARwIh4BBgWCgAgAhsMFiEE+xiuKS/O6847 s/uhQobuV0uS+goFAmhDNjQACgkQQobuV0uS+gp/pwD/VMWAqljM0ExF32sKFp3a cQwzZSpbvHvFtjBhAuJO8ssA/3u6V2fsxl1mPuvYTsNxWMmIsjcHDgaFbEQRyhMj kJkO =ZRc1 -----END PGP PRIVATE KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgp-keys/encapsulated-list.sec000066400000000000000000000201301502127241700250310ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 ECEQBvnW =wUwL -----END PGP PRIVATE KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgp-keys/encapsulated-sender.pub000066400000000000000000000020621502127241700253560ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Comment: 9F29 CA4C F1A4 7561 492C 0737 C7A5 457A 35C5 0082 xo0EYMDjbQEEAOFOAM5DzK9RWjaME7u90bEgAgJ4omMk4LWpvxSf6TTHmT9/ciPG dPwMfci2X4ugon15uZ/XmgNxuK14q912UlEVABtJrxs2u2O4Xofnx856z31beQ0R o01+6j9MqpOF70ZzwbDWiCOaZUwjg3vgYqeMmKC9V4NcEPGICRwiUsSNABEBAAHN B2FkbWluQGHCwA4EEwEKADgWIQSfKcpM8aR1YUksBzfHpUV6NcUAggUCYMDjbQIb AwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRDHpUV6NcUAgummA/9Bl+K695wL ve16vldw9xKI9d0QGhUT1XAMp9nZ2MeD2Z+LBa6SMI6b0yhDG5vPd5LDafy91ed3 X3ZQK62H4M7RIMA89NUCHRb2avt5iLCQwkliXrYthd8+rRhRKZNF0ilesXway7Dt EsQndI1BfJ+U8cYiUWeqfYV60ismvytvGs6NBGDA420BBADUgR0ugu90qdSA7Fwo szSPXYPeGeO42FVj8Ybuu3SY4tuoGtFQKDoMI/sPDVIaKV4HPth86YP5fK5IcX9e TOmZCldcO9yEWLLN0lSRzbS3yqykLJZNHCLvyUmUBv41R5hWK2ynuR+OXJcw3Qxk 2xT4xzngursr93K2qBcGxW14zwARAQABwrYEGAEKACAWIQSfKcpM8aR1YUksBzfH pUV6NcUAggUCYMDjbQIbDAAKCRDHpUV6NcUAgnP1A/0U7UU4p3ygT757DJYaYron ymTPv5gPHYVWq9t/T3N94BhNyXWWY6xAIwbkUt1fHwKlCkRJkJxUqjvWg0GwSV+v iBYDFqrcwLlOOo5XBAptMSkcBGOFQaOJeh1trwWjlduKOwFhuidPXk+aOP0PAm56 vV9oNiZxUMHIwGsGHUtUZg== =NfvK -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgp-keys/public-key-with-third-party-signature.txt000066400000000000000000000045741502127241700307540ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 QKfPPOk= =EqIC -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgpkey_421C19AF8B9C33B8B62D76EBDB2F7E271D773073.sec000066400000000000000000000147261502127241700257530ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- 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 m46seS0yc8ZGUOq12qfulPyqttemDfM= =v+pJ -----END PGP PRIVATE KEY BLOCK----- schleuder-5.0.1/spec/fixtures/openpgpkey_52507B0163A8D9F0094FFE03B1A36F08069E55DE.asc000066400000000000000000000061131502127241700256660ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 nyRAUuvGT7Lx =wHia -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/partially_expired_key.txt000066400000000000000000000041271502127241700234350ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 =xAyq -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/revoked_key.txt000066400000000000000000000035641502127241700213570ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQGiBEjVO7oRBADQvT6wtD2IzzIiK0NbrcilCKCp4MWb8cYXTXguwPQI6y0Nerz4 3YLDaGdb7idJAybemG/BHNVUG90XnCE5XhKKzcjFfklCly+RzRDhJqfBn7z8eH5e dsK6J0X1Vgeo02tqA4xd3EDK8rdqL2yZfl/2egH8+85R3gDk+kqkfEp4pwCgp6VO aEGVKE6G3fX1R6bSUNJybI0D/jhUEj/HXCs44AF+zIKKSzTCwzfdvjHjjs2uoF7t UJa89W7f0EIhFa10RizIfCsivmPxXPxtCvIYGPrssZdFTSgrmy9jU/9pivueyFWY TM8iKelPanlmwP5tzXJt4LRzU3OpsneUSyu60Tpad1trK/2QeqFRNYFVr7OFnGWB rME5BADOU8Zz+Xc65uKcBtjQXd2razdSuil4fgjHicmIwNYnD2aj6lnc+fnfuAYx NPVxUoWfCS4OaSq09OANwFzyWHRGyS/IetaP1kIaxwkOe1j6r0IiF6ZFpZt1DFYF ikgnJBCYDvG95j/8M6Ta05KERkxbjq2QSW4F4s0ax/Au1tztc4hnBCARCAAnBQJM ikhtIB0BUGxlYXNlIHVzZSAweDA2OUU1NURFIGluc3RlYWQuAAoJEAmKyDpMACjp D3sAnjJSaWwbbbPzYug/Jkg+G4Qy+NlKAJ4geMqNPlyPzCci1vqGpTMZwbPm8rQT cGF6IDxwYXpAbmFkaXIub3JnPohiBBMRAgAiAhsDAh4BAheABQJKAbofBwsJCAcD AgEEFQoIAwUWAgMBAAAKCRAJisg6TAAo6TZkAKCVGY5i5Vty1AHB+Oxw74ek33cP lACgjiUAdpcJ0Nb0xjQQT1JPW79LIe6IYAQTEQIAIQUCSNU7ugIbAwcLCQgHAwIB BBUCCAMEFgIDAQIeAQIXgAAKCRAJisg6TAAo6X7NAJiXMEyWnG743jgn7kapIylO wcAQAJ4742pWNW+u2pyrA2tSTnB5B7g9ILkCDQRI1Tu6EAgA0LZj6Cy3t6vCvbyg 8tNXEBA3fED2nAFnmsMmQhlOhy/yqpa3WFnxZhcqi8v81E9bCQrhh3hGIi/aLdJT 6ENxAvDs2vdNAL3Tz3hB/bTGtSLLbgU64xVuMFD7nNeT17GRmabm8utEnKgf6o/c n4+nS1YPQq64b3CpId7Va67nF58fxBGuNgFy7uNIN6zqblHgPWD/cr2hR5hPX9kk xMgS42PFBqoJZ0I+uaTZRf6pGlsEW9OPIfojK1ttRJKLiykwzxZHRW2qVdl+SiWS HH4TtcxG8HNWShdybimBy4fGM2bIk3P6d/t3gQL5/Q5LEBScoDNOYqqUCXcpdGiA Ji+CEwADBggAjuxoMm6Mc2ST4B0RkiA3PE3rCFhoc4gzjksTNyYuZj9w9JSADa3V l73Su4D4YByCOYxVpdde703dFIlLf4/Gbzdx6ejj5g9Nk3ARR3LtEJsno97HIYQK uM0iyTW1cajAJR7gI+K+YQ07G8vFV81t4DlPD9dHJPV6blgy/HgapnthWJBB5L6b MaAwk+inqbkisyaxE4Ov6mOULqhzyDkGZXlgeaa0wUcyg28WRulPGOG/xICpuM6a FNRKvuBbrX2RH2AiLXwAfclrIe7x7a2RgQcb1tcvfxoQIP7gwAGtsP98dPc1pkma 0GMMcA57HTRKe/T/ZrAVqF3aXdBL48Nc3IhJBBgRAgAJBQJI1Tu6AhsMAAoJEAmK yDpMACjpNwsAnRkZCkPV+RkZmOtoc/UgmZ7AtI3mAJ4vDlEFfTV315Uv73jwVJzi 9PDUAQ== =Latx -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/schleuder_at_example_public_key.txt000066400000000000000000000060121502127241700254220ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- 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 /J2/tBYro55u =lep5 -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/fixtures/schleuder_at_example_public_key_minimal.gpg000066400000000000000000000042511502127241700270710ustar00rootroot00000000000000 XF=m֣ҷ$ ?U(mS(CҪMOFx_܍pzvMֺ z{֢`!#-ۖ&kvx9S:¥m\/=wd?{Ogڂ:KћK_b {(s+}CRkg;5ԘlTg57s4lMJxh+ Jkܦ!gCwY,Mxfwk.Pmv!3VnKXZ.;K]y̘9IbIvowb .{i@J2̇%)e&꿏6N ,'Ie#'8{ĵW%,d3ŌymB*wT4t_UtO,,XK1bF^r&>Hߍ,R:lBuele,- zGJ2X h2 nB_!$T#|nQ\htz֫3"B,w*Schleuder TestUser 8"XF=   cPDuC(%_v I¸gٰd6v h+R卞]cnV2ِ7vk&@Xp)+'ƣ2N` 19GmR6NVwl]5HNUZ(Rѐ?[iZE}lZܜfA gS!'Rl)ÐdwAG%fq- goz+GxEȉN)~é[}qXh= qŊŻ1ӱt9V n2]ٖ6Q%ޛl'sqh \d_ kx妆- ?e61ƢTCv36/?[!1qDYq[X_ϱ#"ZXu-!L|ֹ XF=0̖WuŊ|Cj`5T(LV4px \z|fR:ByGE=ls"7DzM֞,@~5dt2i2Y>b|D{zpn"ŒG+;!# I1%$L(z=75sIid*r~K$YWP-$"SFz cҜ]l2 m6N9DT*EOF l dmNJ+j炇nW0b0%.!8!I ,7>MJûr( G.O3\ \$-I͜mՀ h]4O@PyZqWqAkCH)C}_YLtj_̵!El.nlz/0+!$r^VkY }+ ;E/iVA%:@m:`Q;dӾ]Ѩjk}+p d#c: ߗ' a:9'\QOfs$yB32d# y'uOnBug1@rΒ4zxj> B٧?׹C\{2gG^ U ~:?Ⓙ-{ TmQKM")*]|e g.a:+nschleuder-5.0.1/spec/fixtures/schleuder_at_example_public_key_minimal_base64.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-5.0.1/spec/fixtures/signonly_key.txt000066400000000000000000000016651502127241700215620ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mQENBFmC2g4BCADDRv16LnKRbVT9uyjtNkReqM0abvWUKZOBGYr1JV8t8hEeMKwv BpPwh1AZyw+Ihhn/BybwyBDfqmInKWE81bGUl0ZRTaX2i/5JP7uTIcn/7DY3O4rN Hx/h/Xworqq34MhdoxJCsxFlaEyPaNg9bNNVknoKDRpPTMWBCOhLlvJTr2Lr6gbk bDt62fRR6qrwA7Um28jz2j0Dn0bFKv7mDFlQ4zVL89z93oa5G5JRztogWv4FyawM slSrvLhMt+Jw8HOL2w0vyPuZTcITgy8mcyajF2/NnP6aW5p6ujXIw4od3gJtDUOI Uec+ZMFSDvD1jzv42ksHz5D4SkuWWrtffwfxABEBAAG0H3NpZ25vbmx5IDxzaWdu b25seUBleGFtcGxlLm9yZz6JAU4EEwEKADgWIQSxzYuxXCZzxr/Y+ktwss8p4BrV PgUCWYLaDgIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBwss8p4BrVPgj+ B/9hs+NMmdjOM/3KGrL65qVKwAe/zR0xVhmnT+kg9XkCuH1dKT2vae5zkzvU883R DTwk5nFpKFuklYDyTtpOxZkmNXzYIBihFdqWLdg9SO/ydXwFB5dcsinAadWYTNg6 NS6hA5hG7Uc3zush8DzvUvR9JPdKTXLZ4zKNZLJ/SdLQO+e3lgDkfkJ9RH4VNFja Fys4nXVLOAnj39Yngcz6e1xngvqM4453/XgHtxg9AnDj4w09LgdoVfonPwKiLiZ7 f7eFeaHdQr+OWI/0A4Nghzd1SHnBSvlDS7XRiuiu1SYkDWK6M8c7p+It3wHAUeG3 tXmKTndc5oY20DAh14ez7kt6 =SgbM -----END PGP PUBLIC KEY BLOCK----- schleuder-5.0.1/spec/gnupg/000077500000000000000000000000001502127241700155465ustar00rootroot00000000000000schleuder-5.0.1/spec/gnupg/.gpg-v21-migrated000066400000000000000000000000001502127241700205120ustar00rootroot00000000000000schleuder-5.0.1/spec/gnupg/private-keys-v1.d/000077500000000000000000000000001502127241700207375ustar00rootroot00000000000000schleuder-5.0.1/spec/gnupg/private-keys-v1.d/0970A1A2A045304F53EF1810032DDC3F5418C7A7.key000066400000000000000000000035221502127241700260400ustar00rootroot00000000000000(11:private-key(3:rsa(1:n513:m֣ҷ$ ?U(mS(CҪMOFx_܍pzvMֺ z{֢`!#-ۖ&kvx9S:¥m\/=wd?{Ogڂ:KћK_b {(s+}CRkg;5ԘlTg57s4lMJxh+ Jkܦ!gCwY,Mxfwk.Pmv!3VnKXZ.;K]y̘9IbIvowb .{i@J2̇%)e&꿏6N ,'Ie#'8{ĵW%,d3ŌymB*wT4t_UtO,,XK1bF^r&>Hߍ,R:lBuele,- zGJ2X h2 nB_!$T#|nQ\htz֫3"B,w)(1:e3:)(1:d512:S҂3LaZT)+!:fSG$$;$:[ ;_u\nnhfu>tl*Z{}:w,tdĘz,rGx6+nGaiB{"ؗm fo "ݯC;BKqп4* v 5lp I{L'˜v SM!cWs4ǜFL.;άF NW8E2J@yWP*gh}H~L._$(S"µ˜;irͣ:UX1KdQEQ(? է>T)bӐ_aG;"vihI/ QЀ 22-aii ~(х&M$7xs_|7Y}mC>Ay[_slٞDgRJvrhAjF2H14xs&A<Ϟ4RS)(1:p257:x*]nR؁FCʩ}CU~6iJk#YDŽJ4Be. } bV}|W),qJyM߂:Y@!W/?0CDgCgd Cnװ|߁oO3*)"g8=e-Tt{1C%}r#zsy zge{;fa/ 瘑@?9'pi갗ݓ|6)(1:q257:B3"7GM>sec)CsR`fGt55|D YקՇu`0ygR=$8RhIa(mS(}yNf [ cHM] Awj?k"R!R5Ɲ 4i.3MTx`^*S;LS [5M12>G&T 礙c#\}Kix*Ib]zxmĊAԁr@E҅RGj@K)(1:u257:ºhJg!>Rx TfГ¿5y\"b|D{zpn"ŒG+;!# I1%$L(z=75sIid*r~K$YWP-$"SFz cҜ]l2 m6N9DT*EOF l dmNJ+j炇nW0bb|D{zpn"ŒG+;!# I1%$L(z=75sIid*r~K$YWP-$"SFz cҜ]l2 m6N9DT*EOF l dmNJ+j炇nW0b94w!_ڱ D0?ކS7Z6锄NvX|]ct1H,=~]u %cy5D6d]Yp1قx?gI(R7Y!)j^6 6뫺,,8)θWge?,9 W8K;}KZ?0IdUmseh8).QmNEy2NkI=:i*w\xjWhD&E[p^gN1pYbk{h Y6v`0ߚgL3? %"/ό1:_257:cUZM,O&h1^iX"G7VjA+S+6ȸp)9@</d`{x{p)"9BH9wrBLVRّGJx /ISiW<8fg=Ls61Q9t%UI>xLE_{:x2sHcȴo~~u_@1Or8^=ֳ׵3G5IeYb) NFdVо/5@r"n3g#_A[1:_257:*͌KKBo*䎳k.`%$Zd[\T۷p ˨U zqqh]2}FͤZ N:2 #Z\-l'R}{W}R>Ng~UFJ#jQpt/`axKHuRU/= Chx*:BVPQ@E:ZyV*Ѣ!"ZEqփרVCkY篘I*ހIHߍ,R:lBuele,- zGJ2X h2 nB_!$T#|nQ\htz֫3"B,w*Schleuder TestUser 8"XF=   cPDuC(%_v I¸gٰd6v h+R卞]cnV2ِ7vk&@Xp)+'ƣ2N` 19GmR6NVwl]5HNUZ(Rѐ?[iZE}lZܜfA gS!'Rl)ÐdwAG%fq- goz+GxEȉN)~é[}qXh= qŊŻ1ӱt9V n2]ٖ6Q%ޛl'sqh \d_ kx妆- ?e61ƢTCv36/?[!1qDYq[X_ϱ#"ZXu-!L|ְ XF=0̖WuŊ|Cj`5T(LV4px \z|fR:ByGE=ls"7DzM֞,@~5dt2i2Y>b|D{zpn"ŒG+;!# I1%$L(z=75sIid*r~K$YWP-$"SFz cҜ]l2 m6N9DT*EOF l dmNJ+j炇nW0b0%.!8!I ,7>MJûr( G.O3\ \$-I͜mՀ h]4O@PyZqWqAkCH)C}_YLtj_̵!El.nlz/0+!$r^VkY }+ ;E/iVA%:@m:`Q;dӾ]Ѩjk}+p d#c: ߗ' a:9'\QOfs$yB32d# y'uOnBug1@rΒ4zxj> B٧?׹C\{2gG^ U ~:?Ⓙ-{ TmQKM")*]|e g.a:+nschleuder-5.0.1/spec/gnupg/random_seed000066400000000000000000000011301502127241700177440ustar00rootroot00000000000000&~)|ie/^wFQ|8$٭lad>"hȪf -d`!Qcwvc rm zi e T} W n,ʮi>riD?5b󢾭hlD0K'<5sccxYMȺA.)ƅ`6/&Y@1M)dC.Ã~~=W=,/V u)w9d%1QX JA "FA)]lMtaXL,`OXK=+3+B:|Dko^k9Ű_n, (|&r8Hߍ,R:lBuele,- zGJ2X h2 nB_!$T#|nQ\htz֫3"B,wS҂3LaZT)+!:fSG$$;$:[ ;_u\nnhfu>tl*Z{}:w,tdĘz,rGx6+nGaiB{"ؗm fo "ݯC;BKqп4* v 5lp I{L'˜v SM!cWs4ǜFL.;άF NW8E2J@yWP*gh}H~L._$(S"µ˜;irͣ:UX1KdQEQ(? է>T)bӐ_aG;"vihI/ QЀ 22-aii ~(х&M$7xs_|7Y}mC>Ay[_slٞDgRJvrhAjF2H14xs&A<Ϟ4RSx*]nR؁FCʩ}CU~6iJk#YDŽJ4Be. } bV}|W),qJyM߂:Y@!W/?0CDgCgd Cnװ|߁oO3*)"g8=e-Tt{1C%}r#zsy zge{;fa/ 瘑@?9'pi갗ݓ|6B3"7GM>sec)CsR`fGt55|D YקՇu`0ygR=$8RhIa(mS(}yNf [ cHM] Awj?k"R!R5Ɲ 4i.3MTx`^*S;LS [5M12>G&T 礙c#\}Kix*Ib]zxmĊAԁr@E҅RGj@KºhJg!>Rx TfГ¿5y\"8"XF=   cPDuC(%_v I¸gٰd6v h+R卞]cnV2ِ7vk&@Xp)+'ƣ2N` 19GmR6NVwl]5HNUZ(Rѐ?[iZE}lZܜfA gS!'Rl)ÐdwAG%fq- goz+GxEȉN)~é[}qXh= qŊŻ1ӱt9V n2]ٖ6Q%ޛl'sqh \d_ kx妆- ?e61ƢTCv36/?[!1qDYq[X_ϱ#"ZXu-!L|ְXF=0̖WuŊ|Cj`5T(LV4px \z|fR:ByGE=ls"7DzM֞,@~5dt2i2Y>b|D{zpn"ŒG+;!# I1%$L(z=75sIid*r~K$YWP-$"SFz cҜ]l2 m6N9DT*EOF l dmNJ+j炇nW0b94w!_ڱ D0?ކS7Z6锄NvX|]ct1H,=~]u %cy5D6d]Yp1قx?gI(R7Y!)j^6 6뫺,,8)θWge?,9 W8K;}KZ?0IdUmseh8).QmNEy2NkI=:i*w\xjWhD&E[p^gN1pYbk{h Y6v`0ߚgL3? %"/όcUZM,O&h1^iX"G7VjA+S+6ȸp)9@</d`{x{p)"9BH9wrBLVRّGJx /ISiW<8fg=Ls61Q9t%UI>xLE_{:x2sHcȴo~~u_@1Or8^=ֳ׵3G5IeYb) NFdVо/5@r"n3g#_A[*͌KKBo*䎳k.`%$Zd[\T۷p ˨U zqqh]2}FͤZ N:2 #Z\-l'R}{W}R>Ng~UFJ#jQpt/`axKHuRU/= Chx*:BVPQ@E:ZyV*Ѣ!"ZEqփרVCkY篘I*ހI0%.!8!I ,7>MJûr( G.O3\ \$-I͜mՀ h]4O@PyZqWqAkCH)C}_YLtj_̵!El.nlz/0+!$r^VkY }+ ;E/iVA%:@m:`Q;dӾ]Ѩjk}+p d#c: ߗ' a:9'\QOfs$yB32d# y'uOnBug1@rΒ4zxj> B٧?׹C\{2gG^ U ~:?Ⓙ-{ TmQKM")*]|e g.a:+nschleuder-5.0.1/spec/gnupg/trustdb.gpg000066400000000000000000000025201502127241700177330ustar00rootroot00000000000000gpgXFH  5^*/H_n[ `mEGρ7-xt Y"ǂYcPDu! KM'L \ 9[܇schleuder-5.0.1/spec/helpers/000077500000000000000000000000001502127241700160705ustar00rootroot00000000000000schleuder-5.0.1/spec/helpers/api_daemon_spec_helper.rb000066400000000000000000000005041502127241700230610ustar00rootroot00000000000000ENV['RACK_ENV'] = 'test' require 'spec_helper' require 'rack/test' require 'schleuder-api-daemon' module RSpecMixin include Rack::Test::Methods def app() SchleuderApiDaemon end end RSpec.configure { |c| c.include RSpecMixin } # For RSpec 2.x and 3.x def authorize! basic_authorize 'schleuder', 'test_api_key' end schleuder-5.0.1/spec/schleuder-api-daemon/000077500000000000000000000000001502127241700204145ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder-api-daemon/requests/000077500000000000000000000000001502127241700222675ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder-api-daemon/requests/authorization_spec.rb000066400000000000000000000006701502127241700265310ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'authorization via api' do it 'allows un-authorized access to /status.json' do get '/status.json' expect(last_response).to be_ok end it 'blocks un-authorized access to other URLs' do get '/lists.json' expect(last_response.status).to be(401) end it 'allows authorized access' do authorize! get '/status.json' expect(last_response).to be_ok end end schleuder-5.0.1/spec/schleuder-api-daemon/requests/keys_spec.rb000066400000000000000000000143231502127241700246040ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'keys via api' do before :each do @list = List.last || create(:list) end context 'list' do it 'doesn\'t list keys without authentication' do get "/keys.json?list_id=#{@list.id}" expect(last_response.status).to be 401 end it 'does list keys with authentication' do authorize! get "/keys.json?list_id=#{@list.id}" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body).length).to be 1 end end context 'check' do it 'doesn\'t check keys without authentication' do get "/keys/check_keys.json?list_id=#{@list.id}" expect(last_response.status).to be 401 end it 'does check keys with authorization' do @list.import_key(File.read('spec/fixtures/revoked_key.txt')) @list.import_key(File.read('spec/fixtures/signonly_key.txt')) @list.import_key(File.read('spec/fixtures/expired_key.txt')) authorize! get "/keys/check_keys.json?list_id=#{@list.id}" expect(last_response.status).to be 200 result = JSON.parse(last_response.body)['result'] expect(result).to include("This key is expired:\n0x98769E8A1091F36BD88403ECF71A3F8412D83889") expect(result).to include("This key is revoked:\n0x7E783CDE6D1EFE6D2409739C098AC83A4C0028E9") expect(result).to include("This key is not capable of encryption:\n0xB1CD8BB15C2673C6BFD8FA4B70B2CF29E01AD53E") @list.delete_key('0x70B2CF29E01AD53E') @list.delete_key('0x098AC83A4C0028E9') @list.delete_key('0x70B2CF29E01AD53E') end end context 'export' do it 'doesn\'t export keys without authentication' do get "/keys.json?list_id=#{@list.id}" expect(last_response.status).to be 401 end it 'does list keys with authentication' do authorize! get "/keys.json?list_id=#{@list.id}" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body).length).to be 1 end end context 'import' do it 'doesn\'t import keys without authentication' do parameters = {'list_id' => @list.id, 'keymaterial' => File.read('spec/fixtures/bla_foo_key.txt') } expect { post '/keys.json', parameters.to_json expect(last_response.status).to be 401 }.to change{ @list.keys.length }.by 0 end it 'does list keys with authentication' do authorize! parameters = {'list_id' => @list.id, 'keymaterial' => File.read('spec/fixtures/bla_foo_key.txt') } expect { post '/keys.json', parameters.to_json expect(last_response.status).to be 200 }.to change{ @list.keys.length }.by 1 @list.delete_key('0xEBDBE899251F2412') end it 'returns json with key details about imported keys' do authorize! keymaterial = [File.read('spec/fixtures/expired_key.txt'), File.read('spec/fixtures/bla_foo_key.txt')].join("\n") parameters = {'list_id' => @list.id, 'keymaterial' => keymaterial} post '/keys.json', parameters.to_json result = JSON.parse(last_response.body) expect(result).to be_a(Hash) keys = result['keys'] expect(keys).to be_a(Array) expect(keys.size).to eq(2) expect(keys[0]).to be_a(Hash) expect(keys[0]['import_action']).to eq('imported') expect(keys[0]['fingerprint']).to eq('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(keys[0]['summary']).to eq('0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2010-08-14]') expect(keys[1]).to be_a(Hash) expect(keys[1]['import_action']).to eq('imported') expect(keys[1]['fingerprint']).to eq('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') expect(keys[1]['summary']).to eq('0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412 bla@foo 2010-03-14') @list.delete_key('0x98769E8A1091F36BD88403ECF71A3F8412D83889') @list.delete_key('0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') end it 'returns json with empty array in case of useless input' do authorize! parameters = {'list_id' => @list.id, 'keymaterial' => 'something something'} post '/keys.json', parameters.to_json result = JSON.parse(last_response.body) expect(result).to be_a(Hash) keys = result['keys'] expect(keys).to be_a(Array) expect(keys.size).to eq(0) end end context 'delete' do before(:each) do @list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) end it 'doesn\'t delete keys without authentication' do expect { delete "/keys/0xEBDBE899251F2412.json?list_id=#{@list.id}" expect(last_response.status).to be 401 }.to change{ @list.keys.length }.by 0 @list.delete_key('0xEBDBE899251F2412') end it 'does delete keys with authentication' do authorize! expect { delete "/keys/0xEBDBE899251F2412.json?list_id=#{@list.id}" expect(last_response.status).to be 200 }.to change{ @list.keys.length }.by(-1) end end context 'a key with broken utf8 in uid' do context 'already imported' do before(:each) do @list.import_key(File.read('spec/fixtures/broken_utf8_uid_key.txt')) end after(:each) do @list.delete_key('0x1242F6E13D8EBE4A') end it 'does list this key' do authorize! get "/keys.json?list_id=#{@list.id}" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body).length).to be 2 end it 'does get key' do authorize! get "/keys/0x1242F6E13D8EBE4A.json?list_id=#{@list.id}" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body)['fingerprint']).to eq('3102B29989BEE703AE5ED62E1242F6E13D8EBE4A') end it 'does delete key' do authorize! expect { delete "/keys/0x1242F6E13D8EBE4A.json?list_id=#{@list.id}" expect(last_response.status).to be 200 }.to change{ @list.keys.length }.by(-1) end end it 'does add key' do authorize! parameters = {'list_id' => @list.id, 'keymaterial' => File.read('spec/fixtures/broken_utf8_uid_key.txt') } expect { post '/keys.json', parameters.to_json expect(last_response.status).to be 200 }.to change{ @list.keys.length }.by(1) end end end schleuder-5.0.1/spec/schleuder-api-daemon/requests/list_spec.rb000066400000000000000000000016001502127241700245760ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'lists via api' do it 'creates a list' do authorize! list = create(:list) parameters = { email: 'new_testlist@example.com', fingerprint: list.fingerprint } expect { post '/lists.json', parameters.to_json expect(last_response.status).to be 200 }.to change { List.count }.by 1 end it 'shows a list' do authorize! list = create(:list) get "lists/#{list.id}.json" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body)['email']).to eq list.email end it 'correctly finds a list by email-address that starts with a number' do authorize! list = create(:list, email: '9list@hostname') get "lists/#{list.email}.json" expect(last_response.status).to be 200 expect(JSON.parse(last_response.body)['email']).to eq list.email end end schleuder-5.0.1/spec/schleuder-api-daemon/requests/status_spec.rb000066400000000000000000000002561502127241700251540ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'status' do it 'returns status code 200' do get '/status.json' expect(last_response.status).to be 200 end end schleuder-5.0.1/spec/schleuder-api-daemon/requests/subscription_spec.rb000066400000000000000000000053621502127241700263600ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'subscription via api' do before :each do @list = List.last || create(:list) @email = 'someone@localhost' end it 'doesn\'t subscribe new member without authentication' do parameters = {'list_id' => @list.id, :email => @email} expect(@list.subscriptions.size).to be(0) post '/subscriptions.json', parameters.to_json expect(last_response.status).to be 401 expect(@list.reload.subscriptions.size).to be(0) end it 'subscribes new member to a list' do authorize! parameters = {'list_id' => @list.id, :email => @email} expect(@list.subscriptions.size).to be(0) post '/subscriptions.json', parameters.to_json @list.reload expect(last_response.status).to be 201 expect(@list.subscriptions.map(&:email)).to eql([@email]) expect(@list.subscriptions.first.admin?).to be false expect(@list.subscriptions.first.delivery_enabled).to be true end it 'subscribes an admin user' do authorize! parameters = {'list_id' => @list.id, :email => @email, :admin => true} expect(@list.subscriptions.size).to be(0) post '/subscriptions.json', parameters.to_json @list.reload expect(last_response.status).to be 201 expect(@list.subscriptions.map(&:email)).to eql([@email]) expect(@list.subscriptions.first.admin?).to be true expect(@list.subscriptions.first.delivery_enabled).to be true end it 'subscribes an admin user with a truthy value' do authorize! parameters = {'list_id' => @list.id, :email => @email, :admin => '1'} expect(@list.subscriptions.size).to be(0) post '/subscriptions.json', parameters.to_json @list.reload expect(last_response.status).to be 201 expect(@list.subscriptions.map(&:email)).to eql([@email]) expect(@list.subscriptions.first.admin?).to be true expect(@list.subscriptions.first.delivery_enabled).to be true end it 'subscribes an user and unsets delivery flag' do authorize! parameters = {'list_id' => @list.id, :email => @email, :delivery_enabled => false} expect(@list.subscriptions.size).to be(0) post '/subscriptions.json', parameters.to_json @list.reload expect(last_response.status).to be 201 expect(@list.subscriptions.map(&:email)).to eql([@email]) expect(@list.subscriptions.first.admin?).to be false expect(@list.subscriptions.first.delivery_enabled).to be false end it 'unsubscribes members' do subscription = create(:subscription, :list_id => @list.id) authorize! expect(@list.subscriptions.map(&:email)).to eql([subscription.email]) delete "/subscriptions/#{subscription.id}.json" expect(last_response.status).to be 200 expect(@list.reload.subscriptions.map(&:email)).to eql([]) end end schleuder-5.0.1/spec/schleuder-api-daemon/requests/version_spec.rb000066400000000000000000000010231502127241700253070ustar00rootroot00000000000000require 'helpers/api_daemon_spec_helper' describe 'version' do it 'does not return the current schleuder version if not authorized' do get '/version.json' expect(last_response.status).to be 401 expect(last_response.body).not_to eq "{\"version\":\"#{Schleuder::VERSION}\"}" end it 'returns the current schleuder version if authorized' do authorize! get '/version.json' expect(last_response.status).to be 200 expect(last_response.body).to eq "{\"version\":\"#{Schleuder::VERSION}\"}" end end schleuder-5.0.1/spec/schleuder-certificate.pem000066400000000000000000000023611502127241700213710ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDeDCCAmCgAwIBAgIBADANBgkqhkiG9w0BAQsFADA1MQswCQYDVQQGEwJNVzES MBAGA1UECgwJU2NobGV1ZGVyMRIwEAYDVQQLDAlzY2hsZXVkZXIwHhcNMTcwNjA3 MTU1NDU2WhcNMjcwNjA1MTU1NDU2WjA1MQswCQYDVQQGEwJNVzESMBAGA1UECgwJ U2NobGV1ZGVyMRIwEAYDVQQLDAlzY2hsZXVkZXIwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQCkEknthf/N325Yo2Vi+7JAXyewBmgAN+fQapB8KAba8BrN 8TIKi7otJ4GA2TNyYtSlXnEUslXu9YHSevDgrJpnzE0pnOmbDA21yyAZk1kG4NMO fnxw0pjyLIcOF+Do2PCI6K5ipKUZParGiBcdaJpJhRkePw5ILpGu5B3VwgLlQ+H/ vl0dC2+dLrEIVrTs5aUw5EFx/CS5i0KnYDo0K/jvDeFN2xa2tlfVOcQ9ZtVwe+Pl Rl2Kue/RcyIwfE/0M9ijys9xC7lrfmJdeBZ0i0sRoz/eTH5BAUwAGQeMAX4C58Yz qHg9WxIPUsFCvHGqIqg8IWjvfX8s2u6HEpk5HWcrAgMBAAGjgZIwgY8wDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQUB/5P+h1owyWXwRKYnnsFrUyyYEswXQYDVR0j BFYwVIAUB/5P+h1owyWXwRKYnnsFrUyyYEuhOaQ3MDUxCzAJBgNVBAYTAk1XMRIw EAYDVQQKDAlTY2hsZXVkZXIxEjAQBgNVBAsMCXNjaGxldWRlcoIBADANBgkqhkiG 9w0BAQsFAAOCAQEAHRz4AmJWrOe9ieIh8I+qAqUsXtX9Hgh/2RvlNTNs/NvSHb8d xgrncM2ULzWeq+y7Egj61+85x2xXSplgruUFW04Rql01fzBAFafJqk2SvHEzdLIx 1mzHth/d7mFFMg0HYT4iiwhiklH3jrDJHT7EUunRQQEhgunWdXw57BiAppG2bW0U J9LHcGMsxjR6JxbgwQoCZRUBwiGAKd5P4dLc7sELx+wdjU+ChHNf2c7kzVi//Oa2 Ky7jgEuQ0TWmCzAKtx8L1WDu8x0I1QoGrUV0mLVkoCt8nPjDhbxM4hQq/QgkNZ/C wK+nz/RKC99U6r8eaofY2MJA8lr1+69JfzkelQ== -----END CERTIFICATE----- schleuder-5.0.1/spec/schleuder-private-key.pem000066400000000000000000000032171502127241700213500ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEApBJJ7YX/zd9uWKNlYvuyQF8nsAZoADfn0GqQfCgG2vAazfEy Cou6LSeBgNkzcmLUpV5xFLJV7vWB0nrw4KyaZ8xNKZzpmwwNtcsgGZNZBuDTDn58 cNKY8iyHDhfg6NjwiOiuYqSlGT2qxogXHWiaSYUZHj8OSC6RruQd1cIC5UPh/75d HQtvnS6xCFa07OWlMORBcfwkuYtCp2A6NCv47w3hTdsWtrZX1TnEPWbVcHvj5UZd irnv0XMiMHxP9DPYo8rPcQu5a35iXXgWdItLEaM/3kx+QQFMABkHjAF+AufGM6h4 PVsSD1LBQrxxqiKoPCFo731/LNruhxKZOR1nKwIDAQABAoIBAQCYF5wQYzdOUOCp qk5CA7Cpm4ve0RF3oltyCFcHwNMaAZnXbs9El2JumUCjgLUARD17TqDk3qxqZ4uA 4haJL3ey4OBmwt6KrBHJhBKtornUdnUv6nDQ5WiClmRb3CbRssjHIWsGZjnlvBSj FWTYDi94F7nBIBLNNt41kaFWlhK5E0fsPwd5efvNbZpJSwDPZ/iG1dwyZhCGmfu2 HeziDRDxBNDRvPFdwj2l4Oty55vqLXJuQ4nHIHk+DkPd4RtLn8xLFLdm1iFbFSFd k7/0bqpzF3+8o0lXIY3jTKH/Z1C57NAW3enjVADRuqKNZjzIsBKKCF9u/ZkfCLMC EvPlyhYBAoGBANdtJ2O4pgBho6r4Pe/iyeSTS8knr6AU+ViQMfti6qHbBH918wCa ohj5tlEovZedwaKTB58hNy6U7CT5uVK5L8LKXqBYul97ONWlSumj2J++cAsnPCyx p+zYjUm3KI/jUx/vGccOsd1eMGvhYXWBkRU9JG4qL+12W2SgE7Q+XEWxAoGBAML5 CpYt2JGE27HkQBnyaApQAARKShQhUrsHohApZpyZGkwbIqmmjEaCY1nqk70s1LLg nU1vmlnRXNetIhUcj+JtnHKJDqMEvZM/eFxQIXKdsVcD6WQf2u9ZU31TW8uH5G88 eadEHCRrTe/xvEyHlDlIXUkEJwmJDF+36QZQ/sWbAoGBAMNwA4w0uGUgL5usGoTG +uKjvt1/Y5WXcZ8nMjEeTD8Ks8nu98ZUgzqlUQHQNDCYrlMPkJqNR8K62IGzDK4/ 01Skw7Q0yuBUqfspOg082AoUexGjRrRFeFMnIwb9Y48mbQNLp9cvPa3XBZbZodE4 +qaKEcLgAxsrhT6E+1tKN+wBAoGAD5SBEREmzjIUsDlyGeCyCajs52rcUpF7H/Dz NWFpjrf5Tv2YHoBtkzDWKZhCKArOEGE8kLSLXAQL7Dwsjg1TPh/OMaTcI5C8aWjY AGBy28rYIgDxBIw7HYdA0bH4kuIQEgd+HSynJw3gE314s5Dd+lnbAnuvduaZs4hp uZR9V2MCgYB8mmk+KEiD2I3hgkBZSk6eKf1NJOrYO4xdwCb42XArERO4PtiQBdP2 VfsXhPnkkrZSDJ3/vZGw+uiI7u5UD8fl3ATIRMeYYqyiTeFM4ELPToF6hDhtcf/q l0fkQB4Je9KT8zqCDkvytvDjTTXGrxDEPTEPL1OymX4SiIRJaRSEQw== -----END RSA PRIVATE KEY----- schleuder-5.0.1/spec/schleuder.yml000066400000000000000000000010161502127241700171250ustar00rootroot00000000000000database: test: adapter: sqlite3 database: <%= ENV["SCHLEUDER_DB_PATH"] || 'db/test.sqlite3' %> lists_dir: <%= ENV["SCHLEUDER_TMP_DIR"] || '/tmp/schleuder-test/' %> listlogs_dir: <%= ENV["SCHLEUDER_TMP_DIR"] || '/tmp/schleuder-test/' %> keyword_handlers_dir: 'spec/custom_keyword_handlers' smtp_settings: port: 2523 vks_keyserver: http://vkshost sks_keyserver: http://skshost api: tls_cert_file: 'spec/schleuder-certificate.pem' tls_key_file: 'spec/schleuder-private-key.pem' valid_api_keys: 'test_api_key' schleuder-5.0.1/spec/schleuder/000077500000000000000000000000001502127241700164045ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder/errors_spec.rb000066400000000000000000000065601502127241700212660ustar00rootroot00000000000000require 'spec_helper' describe 'Errors' do def signoff t('errors.signoff') end it '::MessageNotFromAdmin shows sensible string in response to to_s()' do expect(Errors::MessageNotFromAdmin.new.to_s).to eql(t('errors.message_not_from_admin') + signoff) end it '::MessageSenderNotSubscribed shows sensible string in response to to_s()' do expect(Errors::MessageSenderNotSubscribed.new.to_s).to eql(t('errors.message_sender_not_subscribed') + signoff) end it '::MessageUnauthenticated shows sensible string in response to to_s()' do expect(Errors::MessageUnauthenticated.new.to_s).to eql(t('errors.message_unauthenticated') + signoff) end it '::MessageUnencrypted shows sensible string in response to to_s()' do expect(Errors::MessageUnencrypted.new.to_s).to eql(t('errors.message_unencrypted') + signoff) end it '::MessageUnsigned shows sensible string in response to to_s()' do expect(Errors::MessageUnsigned.new.to_s).to eql(t('errors.message_unsigned') + signoff) end it '::LoadingListSettingsFailed shows sensible string in response to to_s()' do expect(Errors::LoadingListSettingsFailed.new.to_s).to eql(t('errors.loading_list_settings_failed', config_file: ENV['SCHLEUDER_LIST_DEFAULTS']) + signoff) end it '::DecryptionFailed shows sensible string in response to to_s()' do list = create(:list) expect(Errors::DecryptionFailed.new(list).to_s).to eql(t('errors.decryption_failed', key: list.key.to_s, email: list.sendkey_address) + signoff) end it '::KeyAdduidFailed shows sensible string in response to to_s()' do expect(Errors::KeyAdduidFailed.new('bla').to_s).to eql(t('errors.key_adduid_failed', errmsg: 'bla') + signoff) end it '::KeyGenerationFailed shows sensible string in response to to_s()' do list = create(:list) expect(Errors::KeyGenerationFailed.new(list.listdir, list.email).to_s).to eql(t('errors.key_generation_failed', listdir: list.listdir, listname: list.email) + signoff) end it '::KeywordAdminOnly shows sensible string in response to to_s()' do expect(Errors::KeywordAdminOnly.new('bla').to_s).to eql(t('errors.keyword_admin_only', keyword: 'bla') + signoff) end it '::ListNotFound shows sensible string in response to to_s()' do list = create(:list) expect(Errors::ListNotFound.new(list.email).to_s).to eql(t('errors.list_not_found', email: list.email) + signoff) end it '::ListdirProblem shows sensible string in response to to_s()' do list = create(:list) expect(Errors::ListdirProblem.new(list.listdir, 'not_empty').to_s).to eql(t('errors.listdir_problem.message', dir: list.listdir, problem: t('errors.listdir_problem.not_empty')) + signoff) end it '::MessageEmpty shows sensible string in response to to_s()' do list = create(:list) expect(Errors::MessageEmpty.new(list).to_s).to eql(t('errors.message_empty', request_address: list.request_address) + signoff) end it '::MessageTooBig shows sensible string in response to to_s()' do list = create(:list) expect(Errors::MessageTooBig.new(list).to_s).to eql(t('errors.message_too_big', allowed_size: list.max_message_size_kb) + signoff) end it '::TooManyKeys shows sensible string in response to to_s()' do list = create(:list) expect(Errors::TooManyKeys.new(list.listdir, list.email).to_s).to eql(t('errors.too_many_keys', listdir: list.listdir, listname: list.email) + signoff) end end schleuder-5.0.1/spec/schleuder/integration/000077500000000000000000000000001502127241700207275ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder/integration/cli_spec.rb000066400000000000000000000145131502127241700230410ustar00rootroot00000000000000require 'spec_helper' describe 'cli' do context '#refresh_keys' do it 'updates keys from the keyserver' do resp1 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp1) resp2 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp2) resp3 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/olduid_key_with_newuid.txt')) Typhoeus.stub(/by-fingerprint\/6EE51D78FD0B33DE65CCF69D2104E20E20889F66/).and_return(resp3) list = create(:list) list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/expired_key.txt')) list.import_key(File.read('spec/fixtures/olduid_key.txt')) Cli.new.refresh_keys mail = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } b = mail.first_plaintext_part.body.to_s expect(b).to match(/Refreshing all keys from the keyring of list #{list.email} resulted in this:\n\n/) expect(b).to match(/\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 \[expired: 2017-01-20\]\n/) expect(b).to match(/\nThis key was updated \(new user-IDs and new signatures\):\n0x6EE51D78FD0B33DE65CCF69D2104E20E20889F66 new@example.org 2017-03-25\n/) teardown_list_and_mailer(list) end it 'updates keys from the keyserver for only a specific list' do resp1 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp1) resp2 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp2) resp3 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/olduid_key_with_newuid.txt')) Typhoeus.stub(/by-fingerprint\/6EE51D78FD0B33DE65CCF69D2104E20E20889F66/).and_return(resp3) list1 = create(:list) list2 = create(:list) [list1, list2].each do |list| list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/expired_key.txt')) list.import_key(File.read('spec/fixtures/olduid_key.txt')) end Cli.new.refresh_keys list1.email mail = Mail::TestMailer.deliveries.find { |message| message.to == [list1.admins.first.email] } b = mail.first_plaintext_part.body.to_s expect(b).to match(/Refreshing all keys from the keyring of list #{list1.email} resulted in this:\n\n/) expect(b).to match(/\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 \[expired: 2017-01-20\]\n/) expect(b).to match(/\nThis key was updated \(new user-IDs and new signatures\):\n0x6EE51D78FD0B33DE65CCF69D2104E20E20889F66 new@example.org 2017-03-25\n/) teardown_list_and_mailer(list1) teardown_list_and_mailer(list2) end it 'reports errors from refreshing keys' do resp1 = Typhoeus::Response.new(code: 404, body: 'Not Found') Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp1) Typhoeus.stub(/search=59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp1) resp2 = Typhoeus::Response.new(code: 503, body: 'Internal Server Error') Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp2) Typhoeus.stub(/search=98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp2) list = create(:list) list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/expired_key.txt')) Cli.new.refresh_keys mail = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } expect(mail.first_plaintext_part.decoded).to include("Refreshing all keys from the keyring of list #{list.email} resulted in this") expect(mail.first_plaintext_part.decoded).to include("Error: No key could be found for '59C71FB38AEE22E091C78259D06350440F759BD3'.") expect(mail.first_plaintext_part.decoded).to include('Error while fetching data from the internet: Internal Server Error') teardown_list_and_mailer(list) end it 'warns about file system permissions if it was run as root' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp) expect(Process).to receive(:euid).and_return(0) list = create(:list) output, errors, exitcode = capture_output do Cli.new.refresh_keys(list.email) end expect(errors).to eql('') expect(exitcode).to be(nil) expect(output).to match(/^Warning: this process was run as root/) end end context '#install' do it 'exits if a shell-process failed' do # Rename the Rakefile instead of the DB-file, because changing the latter # caused spurious errors in other tests. File.rename('Rakefile', 'Rakefile.tmp') _, _, exitcode = capture_output do Cli.new.install end File.rename('Rakefile.tmp', 'Rakefile') expect(exitcode).to eql(1) end it 'warns about file system permissions if it was run as root' do expect(Process).to receive(:euid).and_return(0) output, errors, exitcode = capture_output do Cli.new.install end expect(errors).to eql('') expect(exitcode).to be(nil) expect(output).to match(/^Warning: this process was run as root/) end end context '#commands' do it 'exits with a status code of 1 in case the command is not implemented' do run_cli('not-implemented') expect($?.exitstatus).to eq(1) end end context '#check_keys' do it 'warns about file system permissions if it was run as root' do expect(Process).to receive(:euid).and_return(0) output, errors, exitcode = capture_output do Cli.new.check_keys end expect(output).to match(/^Warning: this process was run as root/) end end end schleuder-5.0.1/spec/schleuder/integration/dash_addresses_spec.rb000066400000000000000000000124511502127241700252450ustar00rootroot00000000000000require 'spec_helper' describe 'someone sends an email to a listname-dash-address' do it "sends the list's key as reply to -sendkey" do list = create(:list) list.subscribe('schleuder@example.org', nil, true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.sendkey_address mail.from = 'outside@example.org' mail.body = 'The key, please!' mail.subject = 'key' mail.deliver mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(mail.to_s, list.sendkey_address) expect(output).to be_nil message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['outside@example.org']) signed_message_parts = message.parts[0].parts expect(signed_message_parts.first.body.to_s).to eql('Find the key for this address attached.') expect(message.parts[0].attachments.first.body.to_s).to include(list.fingerprint) expect(message.parts[0].attachments.first.body.to_s).to include('-----BEGIN PGP PUBLIC KEY BLOCK-----') expect(message.in_reply_to).to eql(mail.message_id) end it 'forwards the message to the admins if extension is -owner' do list = create(:list) # owner needs a key so they get email list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.owner_address mail.from = 'outside@example.org' mail.body = 'Please contact me directly!' mail.subject = 'help' mail.deliver mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(mail.to_s, list.owner_address) expect(output).to be_nil raw_msgs = Mail::TestMailer.deliveries raw_msgs.sort_by { |msg| msg.to.first } # reparse the messages so we decrypt and remove all the craft # for easier parsing afterwards message1, message2 = raw_msgs[0..1].collect{|m| Mail.create_message_to_list(m.to_s, list.email, list).setup } expect(message1.to).to eql(['admin@example.org']) expect(message1.subject).to eql('help') expect(message1.parts.first.body.to_s).to include('From: outside@example.org') expect(message1.parts.first.body.to_s).to include('Note: The following message was received for the list-owners.') expect(message1.parts.last.body.to_s).to eql('Please contact me directly!') expect(message2.to).to eql(['schleuder@example.org']) expect(message2.subject).to eql('help') expect(message2.parts.first.body.to_s).to include('From: outside@example.org') expect(message2.parts.first.body.to_s).to include('Note: The following message was received for the list-owners.') expect(message2.parts.last.body.to_s).to eql('Please contact me directly!') end it 'forwards the message to the admins if extension is -bounce' do list = create(:list) list.subscribe('admin@example.org', nil, true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.bounce_address mail.from = 'mailer-daemon@example.org' mail.body = 'delivery failure' mail.subject = 'something' mail.deliver mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(mail.to_s, list.bounce_address) expect(output).to be_nil message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['admin@example.org']) expect(message.subject).to eql(I18n.t('automated_message_subject')) signed_message_parts = message.parts[0].parts expect(signed_message_parts.first.body.to_s).to eql(I18n.t('forward_automated_message_to_admins')) expect(signed_message_parts.last.mime_type).to eql('message/rfc822') expect(signed_message_parts.last.body.to_s).to include('From: mailer-daemon@example.org') expect(signed_message_parts.last.body.to_s).to include(mail.message_id) expect(signed_message_parts.last.body.to_s).to include('Subject: something') expect(signed_message_parts.last.body.to_s).to include('delivery failure') end it "forwards the message to the admins if extension is -bounce and it's a real bounce mail" do list = create(:list) list.subscribe('admin@example.org', nil, true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new(File.read('spec/fixtures/mails/bounce.eml')) mail.to = list.owner_address mail.deliver mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(mail.to_s, list.bounce_address) expect(output).to be_nil message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['admin@example.org']) expect(message.subject).to eql(I18n.t('automated_message_subject')) signed_message_parts = message.parts[0].parts expect(signed_message_parts.first.body.to_s).to eql(I18n.t('forward_automated_message_to_admins')) expect(signed_message_parts.last.mime_type).to eql('message/rfc822') expect(signed_message_parts.last.body.to_s).to include('Mailer-Daemon@schleuder.example.org') expect(signed_message_parts.last.body.to_s).to include(mail.message_id) expect(signed_message_parts.last.body.to_s).to include('Subject: bounce test') expect(signed_message_parts.last.body.to_s).to include('mailbox is full') end end schleuder-5.0.1/spec/schleuder/integration/filters_spec.rb000066400000000000000000000250161502127241700237420ustar00rootroot00000000000000require 'spec_helper' describe 'running filters' do context '.max_message_size' do it 'bounces to big mails' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = '+' * (1024 * list.max_message_size_kb) mail.deliver big_email = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(big_email.to_s, list.email) expect(output.message).to include(I18n.t('errors.message_too_big', allowed_size: list.max_message_size_kb)) teardown_list_and_mailer(list) end end context '.fix_exchange_messages!' do it 'accepts an invalid pgp/mime Exchange message' do list = create(:list) list.subscribe('admin@example.org', nil, true) # so we can easily parse the outgoing mail list.send_encrypted_only = false list.save start_smtp_daemon message_path = 'spec/fixtures/mails/exchange.eml' error = run_schleuder(:work, list.email, message_path) mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 exchange = Mail.read(mails.first) expect(exchange.to).to eql(['admin@example.org']) expect(exchange.parts.first.parts.last.decoded).to include("foo\n") stop_smtp_daemon end it 'accepts a valid plain-text message' do list = create(:list) list.subscribe('admin@example.org', nil, true) # so we can easily parse the outgoing mail list.send_encrypted_only = false list.save start_smtp_daemon message_path = 'spec/fixtures/mails/exchange_no_parts.eml' error = run_schleuder(:work, list.email, message_path) mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 exchange = Mail.read(mails.first) expect(exchange.to).to eql(['admin@example.org']) expect(exchange.parts.first.parts.last.decoded).to include('bla-vla') stop_smtp_daemon end end context '.strip_html_from_alternative!' do it 'strips HTML-part from multipart/alternative-message that contains ascii-armored PGP-data' do list = create(:list) list.subscribe('admin@example.org', nil, true) # so we can easily parse the outgoing mail list.send_encrypted_only = false list.save start_smtp_daemon mail = Mail.new mail.to = list.email mail.from = 'outside@example.org' content = encrypt_string(list, 'blabla') mail.text_part = content mail.html_part = "

#{content}

" mail.subject = 'test' error = nil with_tmpfile(mail.to_s) do |fn| error = run_schleuder(:work, list.email, fn) end mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 htmlmail = Mail.read(mails.first) expect(htmlmail.to).to eql(['admin@example.org']) signed_parts = htmlmail.parts[0].parts expect(signed_parts[0].body.to_s).to include("Note: This message included an alternating HTML-part that contained\n PGP-data. The HTML-part was removed to enable parsing the message more\n properly.\n") # why is this double wrapped? expect(signed_parts[1].parts[0][:content_type].content_type).to eql('text/plain') expect(signed_parts[1].parts[0].body.to_s).to eql("blabla\n") stop_smtp_daemon end it 'does NOT strip HTML-part from multipart/alternative-message that does NOT contain ascii-armored PGP-data' do list = create(:list) list.subscribe('admin@example.org', nil, true) # so we can easily parse the outgoing mail list.send_encrypted_only = false list.save start_smtp_daemon mail = Mail.new mail.to = list.email mail.from = 'outside@example.org' content = 'blabla' mail.text_part = content mail.html_part = "

#{content}

" mail.subject = 'test' error = nil with_tmpfile(mail.to_s) do |fn| error = run_schleuder(:work, list.email, fn) end mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 htmlmail = Mail.read(mails.first) expect(htmlmail.to).to eql(['admin@example.org']) # this is double wrapped signed_parts = htmlmail.parts[0].parts[1].parts expect(signed_parts[0][:content_type].content_type).to eql('text/plain') expect(signed_parts[0].body.to_s).to eql('blabla') expect(signed_parts[1][:content_type].content_type).to eql('text/html') expect(signed_parts[1].body.to_s).to eql('

blabla

') stop_smtp_daemon end end context('.key_auto_import_from_autocrypt_header') do it('successfully validates a signature, whose previously unknown key is in the autocrypt-header') do list = create(:list, send_encrypted_only: false, key_auto_import_from_email: true) list.subscribe('me@localhost', nil, true) tmp_gnupg_home = Dir.mktmpdir ENV['GNUPGHOME'] = tmp_gnupg_home gpg = GPGME::Ctx.new gpg.keyimport(File.read('spec/fixtures/openpgp-keys/FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A-abcde_example_org.sec')) keyblock = gpg.find_keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').first.export(armor: true).read keydata_base64 = Base64.encode64(keyblock).gsub("\n", '') mail = Mail.new mail.from = 'abcde@example.org' mail.body = 'something' mail.to = list.email mail.header['Autocrypt'] = "addr=schleuder@example.org; prefer-encrypt=mutual; keydata=#{keydata_base64}" mail.gpg({ encrypt: false, sign: true, sign_as: 'FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A' }) mail.deliver expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(0) signed_email = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear Schleuder::Runner.new.run(signed_email.to_s, list.email) mail_to_subscriber = Mail::TestMailer.deliveries.first pseudoheaders_part = mail_to_subscriber.parts.first.parts.first expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(1) expect(pseudoheaders_part.body.to_s).to include("Note: This key was newly added from this email:\n 0xFB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A") expect(pseudoheaders_part.body.to_s).to include('Sig: Good signature from 4286EE574B92FA0A abcde ') ensure FileUtils.rm_rf(tmp_gnupg_home) end end context('.key_auto_import_from_attachments') do it('successfully validates a signature, whose previously unknown key is attached') do list = create(:list, send_encrypted_only: false, key_auto_import_from_email: true) list.subscribe('me@localhost', nil, true) tmp_gnupg_home = Dir.mktmpdir ENV['GNUPGHOME'] = tmp_gnupg_home gpg = GPGME::Ctx.new gpg.keyimport(File.read('spec/fixtures/openpgp-keys/FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A-abcde_example_org.sec')) keyblock = gpg.find_keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').first.export(armor: true).read mail = Mail.new mail.from = 'abcde@example.org' mail.body = 'something' mail.to = list.email mail.add_file({filename: 'abcde.asc', content: keyblock, mime_type: 'application/pgp-keys'}) mail.gpg({ encrypt: false, sign: true, sign_as: 'FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A' }) mail.deliver expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(0) signed_email = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear Schleuder::Runner.new.run(signed_email.to_s, list.email) mail_to_subscriber = Mail::TestMailer.deliveries.first pseudoheaders_part = mail_to_subscriber.parts.first.parts.first expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(1) expect(pseudoheaders_part.body.to_s).to include("Note: This key was newly added from this email:\n 0xFB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A") expect(pseudoheaders_part.body.to_s).to include('Sig: Good signature from 4286EE574B92FA0A abcde ') ensure FileUtils.rm_rf(tmp_gnupg_home) end it('successfully validates a signature, whose previously unknown key is attached, from an encrypted+signed message') do list = create(:list, send_encrypted_only: false, key_auto_import_from_email: true) list.subscribe('me@localhost', nil, true) list_key = list.key.export tmp_gnupg_home = Dir.mktmpdir ENV['GNUPGHOME'] = tmp_gnupg_home gpg = GPGME::Ctx.new gpg.keyimport(File.read('spec/fixtures/openpgp-keys/FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A-abcde_example_org.sec')) mail = Mail.new mail.from = 'abcde@example.org' mail.body = 'something' mail.to = list.email keyblock = gpg.find_keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').first.export(armor: true).read mail.add_file({filename: 'abcde.asc', content: keyblock, mime_type: 'application/pgp-keys'}) gpg.keyimport(list_key) mail.gpg({ encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: 'FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A' }) mail.deliver ENV['GNUPGHOME'] = list.listdir expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(0) signed_email = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear Schleuder::Runner.new.run(signed_email.to_s, list.email) mail_to_subscriber = Mail::TestMailer.deliveries.first pseudoheaders_part = mail_to_subscriber.parts.first.parts.first expect(list.keys('FB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A').size).to eql(1) expect(pseudoheaders_part.body.to_s).to include("Note: This key was newly added from this email:\n 0xFB18AE292FCEEBCE3BB3FBA14286EE574B92FA0A") expect(pseudoheaders_part.body.to_s).to include('Enc: Encrypted') expect(pseudoheaders_part.body.to_s).to include('Sig: Good signature from 4286EE574B92FA0A abcde ') ensure FileUtils.rm_rf(tmp_gnupg_home) end end end schleuder-5.0.1/spec/schleuder/integration/keywords_spec.rb000066400000000000000000003447621502127241700241550ustar00rootroot00000000000000# coding: utf-8 require 'spec_helper' describe 'user sends keyword' do it 'x-subscribe without attributes' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s*$/) expect(message.to_s).to include('Admin? false') expect(message.to_s).to include('Email-delivery enabled? true') expect(subscription).to be_present expect(subscription.fingerprint).to be_blank expect(subscription.admin).to eql(false) expect(subscription.delivery_enabled).to eql(true) teardown_list_and_mailer(list) end it 'x-subscribe with attributes' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint} true false" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? true') expect(message.to_s).to include('Email-delivery enabled? false') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(true) expect(subscription.delivery_enabled).to eql(false) teardown_list_and_mailer(list) end it 'x-subscribe with one attribute and spaces-separated fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint.dup.insert(4, ' ')} true" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? true') expect(message.to_s).to include('Email-delivery enabled? true') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(true) expect(subscription.delivery_enabled).to eql(true) teardown_list_and_mailer(list) end it 'x-subscribe without attributes, but with spaces-separated fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint.dup.insert(4, ' ')}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? false') expect(message.to_s).to include('Email-delivery enabled? true') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(false) expect(subscription.delivery_enabled).to eql(true) teardown_list_and_mailer(list) end it 'x-subscribe with attributes and spaces-separated fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint.dup.insert(4, ' ')} true false" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? true') expect(message.to_s).to include('Email-delivery enabled? false') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(true) expect(subscription.delivery_enabled).to eql(false) teardown_list_and_mailer(list) end it "x-subscribe with attributes (first one 'false') and spaces-separated fingerprint" do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint.dup.insert(4, ' ')} false false" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? false') expect(message.to_s).to include('Email-delivery enabled? false') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(false) expect(subscription.delivery_enabled).to eql(false) teardown_list_and_mailer(list) end it "x-subscribe with attributes (last one 'true') and spaces-separated fingerprint" do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint.dup.insert(4, ' ')} false true" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been subscribed') expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint}/) expect(message.to_s).to include('Admin? false') expect(message.to_s).to include('Email-delivery enabled? true') expect(subscription).to be_present expect(subscription.fingerprint).to eql(list.fingerprint) expect(subscription.admin).to eql(false) expect(subscription.delivery_enabled).to eql(true) teardown_list_and_mailer(list) end it 'x-subscribe with invalid arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test@example.org 0x#{list.fingerprint}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).not_to include('test@example.org has been subscribed') expect(message.to_s).not_to include('translation missing') expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.subscribe_requires_arguments')) expect(subscription).to be_blank teardown_list_and_mailer(list) end it 'x-subscribe without arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE:" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).not_to include('translation missing') expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.subscribe_requires_arguments')) expect(subscription).to be_blank teardown_list_and_mailer(list) end it 'x-unsubscribe without argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) list.subscribe('admin@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = 'schleuder@example.org' gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3' } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE:" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('schleuder@example.org has been unsubscribed') expect(subscription).to be_blank teardown_list_and_mailer(list) end it 'x-unsubscribe with invalid argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE: test@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org is not subscribed') teardown_list_and_mailer(list) end it 'x-unsubscribe' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('test@example.org') ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE: test@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('test@example.org has been unsubscribed') expect(subscription).to be_blank teardown_list_and_mailer(list) end it "x-unsubscribe doesn't unsubscribe last admin" do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('test@example.org') ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE: schleuder@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.cannot_unsubscribe_last_admin', email: 'schleuder@example.org')) expect(list.subscriptions.size).to be(2) teardown_list_and_mailer(list) end it 'x-set-fingerprint with own email-address and valid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder@example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for schleuder@example.org set to C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-set-fingerprint with own email-address and valid, spaces-separated fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder@example.org C4D6 0F88 3378 9C7C AA44 496F D3FF A661 3AB1 0ECE" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for schleuder@example.org set to C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-set-fingerprint without email-address and with valid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: C4D60F8833789C7CAA44496FD3FFA6613AB10ECE" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for schleuder@example.org set to C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-set-fingerprint with other email-address and valid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('test@example.org') list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: test@example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for test@example.org set to C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-set-fingerprint with other email-address and valid fingerprint as non-admin' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3') list.subscribe('test@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = 'schleuder@example.org' gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3' } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: test@example.org 59C71FB38AEE22E091C78259D06350440F759BD3" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Only admins may set fingerprints of subscriptions other than their own') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-set-fingerprint with email-address but without fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder@example.org " mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_requires_valid_fingerprint', fingerprint: '' )) expect(subscription).to be_present expect(subscription.fingerprint).to eql('59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-set-fingerprint with email-address but without valid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder@example.org 59C71FB38AEE22E091C78259D0" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_requires_valid_fingerprint', fingerprint: '59c71fb38aee22e091c78259d0' )) expect(subscription).to be_present expect(subscription.fingerprint).to eql('59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-set-fingerprint without email-address and with invalid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: blabla" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t( 'keyword_handlers.subscription_management.set_fingerprint_requires_valid_fingerprint', fingerprint: 'blabla' )) expect(subscription.fingerprint).to eql('59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-set-fingerprint with not-subscribed email-address and valid fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: bla@example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('bla@example.org is not subscribed') teardown_list_and_mailer(list) end it 'x-set-fingerprint without argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: " mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.set_fingerprint_requires_arguments')) teardown_list_and_mailer(list) end it 'x-unset-fingerprint without argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: " mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.unset_fingerprint_requires_arguments')) teardown_list_and_mailer(list) end it 'x-unset-fingerprint with other email-address as admin' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('test@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: test@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for test@example.org removed.') expect(subscription).to be_present expect(subscription.fingerprint.blank?).to be_truthy teardown_list_and_mailer(list) end it 'x-unset-fingerprint with own email-address as admin but without force' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: schleuder@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.subscription_management.unset_fingerprint_requires_arguments')) expect(subscription).to be_present expect(subscription.fingerprint).to eql('59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-unset-fingerprint with own email-address as admin and force' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: schleuder@example.org force" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'schleuder@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Fingerprint for schleuder@example.org removed.') expect(subscription).to be_present expect(subscription.fingerprint.blank?).to be_truthy teardown_list_and_mailer(list) end it 'x-unset-fingerprint with not-subscribed email-address' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: bla@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('bla@example.org is not subscribed') teardown_list_and_mailer(list) end it 'x-unset-fingerprint with other email-address as non-admin' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3') list.subscribe('test@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', true) list.import_key(File.read('spec/fixtures/example_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = 'schleuder@example.org' gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3' } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-unset-fingerprint: test@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup subscription = list.subscriptions.where(email: 'test@example.org').first expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Only admins may remove fingerprints of subscriptions other than their own') expect(subscription).to be_present expect(subscription.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') teardown_list_and_mailer(list) end it 'x-list-subscriptions without arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-subscriptions:" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(3) expect(message.to_s).to include('schleuder@example.org 0x59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-list-subscriptions without arguments but with admin-notification' do list = create(:list, keywords_admin_notify: ['list-subscriptions']) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.subscribe('user@example.org') ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-subscriptions:" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first notification = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup raw = Mail::TestMailer.deliveries[1] response = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expected_text = "Subscriptions:\n\nschleuder@example.org\t0x59C71FB38AEE22E091C78259D06350440F759BD3\nuser@example.org" expect(Mail::TestMailer.deliveries.size).to eql(2) expect(notification.to).to eql(['schleuder@example.org']) expect(notification.first_plaintext_part.body.to_s).to eql("schleuder@example.org sent this keyword:\n\nlist-subscriptions: \n\n\n...and received this response:\n\n#{expected_text}\n") expect(response.to).to eql(['schleuder@example.org']) expect(response.first_plaintext_part.body.to_s).to eql(expected_text) teardown_list_and_mailer(list) end it 'x-list-subscriptions with matching argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-subscriptions: example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(3) expect(message.to_s).to include('schleuder@example.org 0x59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-list-subscriptions with non-matching argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-subscriptions: blabla" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(1) expect(message.to_s).to include('Your message resulted in no output') teardown_list_and_mailer(list) end it 'x-fetch-key with invalid input' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: lala!" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.to_s).to include('Invalid input.') teardown_list_and_mailer(list) end it 'x-fetch-key with email address' do key = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-email\/admin%40example.org/).and_return(key) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: admin@example.org" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num + 1) expect(message.first_plaintext_part.body.to_s).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'x-fetch-key with unknown email-address' do resp = Typhoeus::Response.new(code: 404, body: 'Not found') Typhoeus.stub(/by-email\/something%40localhost/).and_return(resp) Typhoeus.stub(/search=something%40localhost/).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: something@localhost" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.to_s).to include("Error: No key could be found for 'something@localhost'.") teardown_list_and_mailer(list) end it 'x-fetch-key with URL' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/keys\/example.asc/).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: http://somehost/keys/example.asc" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num + 1) expect(message.first_plaintext_part.body.to_s).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'x-fetch-key with invalid URL' do resp = Typhoeus::Response.new(code: 404, body: 'Not found') Typhoeus.stub(/foo/).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) url = 'http://somehost/foo' mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: #{url}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.to_s).to include("Error: There's nothing at <#{url}> (404 Not Found).") teardown_list_and_mailer(list) end it 'x-fetch-key with unknown fingerprint' do resp = Typhoeus::Response.new(code: 404, body: 'Not found') Typhoeus.stub(/by-fingerprint\/0000000000000000000000000000000000000000/).and_return(resp) Typhoeus.stub(/search=0x0000000000000000000000000000000000000000/).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x0000000000000000000000000000000000000000" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.to_s).to include("Error: No key could be found for '0x0000000000000000000000000000000000000000'.") teardown_list_and_mailer(list) end it 'x-fetch-key with fingerprint' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x98769E8A1091F36BD88403ECF71A3F8412D83889" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num + 1) expect(message.first_plaintext_part.body.to_s).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'x-fetch-key with fingerprint of unchanged key' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/i).and_return(resp) list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.first_plaintext_part.body.to_s).to eql("This key was fetched (unchanged):\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06\n") teardown_list_and_mailer(list) end it 'x-fetch-key without arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: " mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.find { |message| message.to == [list.admins.first.email] } message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num) expect(message.to_s).not_to include('translation missing') expect(message.first_plaintext_part.body.to_s).to eql(I18n.t('keyword_handlers.key_management.fetch_key_requires_arguments')) teardown_list_and_mailer(list) end it 'x-resend' do list = create(:list, public_footer: "-- \nblablabla") list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first resent_message = raw.verify resent_message_body = resent_message.parts.map { |p| p.body.to_s }.join raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Resent: Unencrypted to someone@example.org') expect(resent_message.to).to include('someone@example.org') expect(resent_message.to_s).not_to include('Resent: Unencrypted to someone@example.org') expect(resent_message_body).to eql(content_body + list.public_footer.to_s) teardown_list_and_mailer(list) end it 'does not parse keywords once the mail body started' do list = create(:list, public_footer: "-- \nblablabla") list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = < list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\nHello Again!\n\nLooking forward to meeting you!" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end expect(Mail::TestMailer.deliveries.length).to eql(1) raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.to).to eql([list.admins.first.email]) expect(message.to_s).to include('Error: Invalid email-address for resending: hello') expect(message.to_s).to include('Error: Invalid email-address for resending: again!') expect(message.to_s).not_to include('Resent: ') teardown_list_and_mailer(list) end it 'x-resend does not include internal_footer' do list = create( :list, internal_footer: "-- \nsomething private", public_footer: "-- \nsomething public" ) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first resent_message = raw.verify resent_message_body = resent_message.parts.map { |p| p.body.to_s }.join expect(resent_message_body).not_to include(list.internal_footer) expect(resent_message_body).to eql(content_body + list.public_footer.to_s) teardown_list_and_mailer(list) end it 'x-resend with iso-8859-1 body' do list = create(:list, public_footer: "-- \nblablabla") list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again! ¡Hola!\r\n" mail.charset = 'iso-8859-1' mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}".encode('iso-8859-1') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first resent_message = raw.verify raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Resent: Unencrypted to someone@example.org') expect(message.parts[1].decoded.force_encoding(message.parts[1].charset)).to eql(content_body.encode(message.parts[1].charset)) expect(resent_message.to).to include('someone@example.org') expect(resent_message.to_s).not_to include('Resent: Unencrypted to someone@example.org') expect(resent_message.parts[0].decoded).to eql(content_body.encode(resent_message.parts[0].charset)) expect(resent_message.parts[1].decoded).to eql(list.public_footer.to_s) teardown_list_and_mailer(list) end it 'x-resend with utf-8 body and umlauts' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "This is a test\r\nAnd here are some umlauts:ÄäÖöÜüß" mail.charset = 'utf-8' mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}".encode('utf-8') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first resent_message = raw.verify raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Resent: Unencrypted to someone@example.org') expect(message.parts[1].decoded.force_encoding(message.parts[1].charset)).to eql(content_body.encode(message.parts[1].charset)) expect(resent_message.to).to include('someone@example.org') expect(resent_message.to_s).not_to include('Resent: Unencrypted to someone@example.org') expect(resent_message.parts[0].decoded).to eql(content_body.encode(resent_message.parts[0].charset)) teardown_list_and_mailer(list) end it 'x-resend with admin-notification' do list = create(:list, keywords_admin_notify: ['resend']) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries[1] notification = Mail.create_message_to_list(raw.to_s, list.email, list).setup raw = Mail::TestMailer.deliveries[2] message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(Mail::TestMailer.deliveries.size).to eql(3) expect(notification.to).to eql(['schleuder@example.org']) expect(notification.first_plaintext_part.body.to_s).to eql("schleuder@example.org sent this keyword to the list:\n\nresend: someone@example.org\n") expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Resent: Unencrypted to someone@example.org') teardown_list_and_mailer(list) end it 'x-resend with admin-notification and admin has delivery disabled' do list = create(:list, keywords_admin_notify: ['resend']) list.subscribe('user@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3') list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true, false) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend: someone@example.org\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries[1] notification = Mail.create_message_to_list(raw.to_s, list.email, list).setup raw = Mail::TestMailer.deliveries[2] message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(Mail::TestMailer.deliveries.size).to eql(3) expect(notification.to).to eql(['admin@example.org']) expect(notification.first_plaintext_part.body.to_s).to eql("admin@example.org sent this keyword to the list:\n\nresend: someone@example.org\n") expect(message.to).to eql(['user@example.org']) expect(message.to_s).to include('Resent: Unencrypted to someone@example.org') teardown_list_and_mailer(list) end it 'x-resend-encrypted-only with matching key' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-encrypted-only: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(resent_message.to).to eql(['bla@foo']) expect(resent_message.content_type).to match(/^multipart\/encrypted.*application\/pgp-encrypted/) expect(message.first_plaintext_part.body.to_s).to include("Resent: Encrypted to bla@foo (87E65ED2081AE3D16BE4F0A5EBDBE899251F2412)") teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only with matching key' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(resent_message.cc).to eql(['bla@foo']) expect(resent_message.content_type).to match(/^multipart\/encrypted.*application\/pgp-encrypted/) expect(message.first_plaintext_part.body.to_s).to include("ResentCc: Encrypted to bla@foo (87E65ED2081AE3D16BE4F0A5EBDBE899251F2412)") teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only to 2 addresses with matching keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: schleuder@example.org bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(resent_message.cc).to eql(['schleuder@example.org', 'bla@foo']) expect(resent_message.content_type).to match(/^multipart\/encrypted.*application\/pgp-encrypted/) expect(message.first_plaintext_part.body.to_s).to include("ResentCc: Encrypted to schleuder@example.org (59C71FB38AEE22E091C78259D06350440F759BD3), bla@foo (87E65ED2081AE3D16BE4F0A5EBDBE899251F2412)") teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only to 2 addresses with one missing keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir expect(list.keys('bla@foo').size).to eql(0) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: schleuder@example.org bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end expect(Mail::TestMailer.deliveries.size).to eql(1) raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.first_plaintext_part.body.to_s).to include("Sig: Good signature from D06350440F759BD3 Schleuder TestUser Enc: Encrypted Error: Resending to failed (0 keys found, of which 0 can be used. Unencrypted sending not allowed). Error: Resending to aborted due to other errors.") teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only to 3 addresses with one missing keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) expect(list.keys('bla@foo').size).to eql(1) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: schleuder@example.org foo@bla bla@foo\n\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end expect(Mail::TestMailer.deliveries.size).to eql(1) raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.first_plaintext_part.body.to_s).to include("Error: Resending to failed (0 keys found, of which 0 can be used. Unencrypted sending not allowed). Error: Resending to aborted due to other errors. Error: Resending to aborted due to other errors.") teardown_list_and_mailer(list) end it 'x-resend-cc to 2 addresses with missing keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc: foo@bla bla@foo\n\n#{content_body}" expect(list.keys('foo@bla').size).to eql(0) expect(list.keys('bla@foo').size).to eql(0) mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(resent_message.cc).to eql(['foo@bla', 'bla@foo']) expect(resent_message.content_type).to match(/^multipart\/signed.*application\/pgp-signature/) expect(message.first_plaintext_part.body.to_s).to include('ResentCc: Unencrypted to foo@bla, bla@foo') teardown_list_and_mailer(list) end it 'x-resend-cc to 2 addresses with one missing keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc: foo@bla bla@foo\n\n#{content_body}" expect(list.keys('foo@bla').size).to eql(0) expect(list.keys('bla@foo').size).to eql(1) mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(resent_message.cc).to eql(['foo@bla', 'bla@foo']) expect(resent_message.content_type).to match(/^multipart\/signed.*application\/pgp-signature/) expect(message.first_plaintext_part.body.to_s).to include('ResentCc: Unencrypted to foo@bla, bla@foo') teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only to 2 addresses with missing keys' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: foo@bla bla@foo\n\n#{content_body}" expect(list.keys('foo@bla').size).to eql(0) expect(list.keys('bla@foo').size).to eql(0) mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end expect(Mail::TestMailer.deliveries.size).to eql(1) raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(0) expect(message.first_plaintext_part.body.to_s).to include("Error: Resending to failed (0 keys found, of which 0 can be used. Unencrypted sending not allowed). Error: Resending to failed (0 keys found, of which 0 can be used. Unencrypted sending not allowed).") teardown_list_and_mailer(list) end it 'x-resend-encrypted-only with two matching keys, one of which is expired' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/expired_key.txt')) list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-encrypted-only: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(2) expect(resent_message.to).to eql(['bla@foo']) expect(resent_message.content_type).to match(/^multipart\/encrypted.*application\/pgp-encrypted/) expect(message.first_plaintext_part.body.to_s).to include("Resent: Encrypted to bla@foo (87E65ED2081AE3D16BE4F0A5EBDBE899251F2412)") teardown_list_and_mailer(list) end it 'x-resend-unencrypted with matching key' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-unencrypted: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end resent_message = Mail::TestMailer.deliveries.first raw = Mail::TestMailer.deliveries.last message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(resent_message.to).to eql(['bla@foo']) expect(resent_message.content_type).to_not match(/^multipart\/encrypted.*application\/pgp-encrypted/) expect(resent_message.first_plaintext_part.body.to_s).to include('Hello again!') expect(message.first_plaintext_part.body.to_s).to include('Resent: Unencrypted to bla@foo') teardown_list_and_mailer(list) end it 'x-resend-encrypted-only with expired key' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/expired_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-encrypted-only: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(message.first_plaintext_part.to_s).to include("Error: Resending to failed (1 keys found, of which 0 can be used.\r\n Unencrypted sending not allowed).") teardown_list_and_mailer(list) end it 'x-resend-cc-encrypted-only with expired key' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir list.import_key(File.read('spec/fixtures/expired_key.txt')) mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" mail.body = "x-list-name: #{list.email}\nX-resend-cc-encrypted-only: bla@foo\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(list.keys('bla@foo').size).to eql(1) expect(message.first_plaintext_part.to_s).to include("Error: Resending to failed (1 keys found, of which 0 can be used.\r\n Unencrypted sending not allowed).") teardown_list_and_mailer(list) end it 'x-resend with invalid recipient' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = "Hello again!\n" invalid_recipient = '`ls`bla' mail.body = "x-list-name: #{list.email}\nX-resend: #{invalid_recipient}\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end delivered_emails = Mail::TestMailer.deliveries raw = delivered_emails.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(delivered_emails.size).to eql(1) expect(message.to_s).not_to include('Resent: Unencrypted to someone@example.org') expect(message.to_s).to include("Error: Invalid email-address for resending: #{invalid_recipient}") teardown_list_and_mailer(list) end it 'x-sign-this with inline text' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) signed_text = "signed\nsigned\nsigned\n\n" mail.body = "x-list-name: #{list.email}\nx-sign-this:\n\n#{signed_text}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.to_s.gsub("\r", '')).to match(/BEGIN PGP SIGNED MESSAGE-----\nHash: SHA(256|512)\n\n#{signed_text}-----BEGIN PGP SIGNATURE/) teardown_list_and_mailer(list) end it 'x-sign-this with attachments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) keywords = Mail::Part.new keywords.body = "\n\nx-list-name: #{list.email}\nx-sign-this:" mail.parts << keywords signed_content = File.read('spec/fixtures/example_key.txt') mail.attachments['example_key.txt'] = { mime_type: 'application/pgp-key', content: signed_content } mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup signature = message.attachments.first.body.to_s # list.gpg.verify() results in a "Bad Signature". The sign-this keyword-handler # also uses GPGME::Crypto, apparently that makes a difference. crypto = GPGME::Crypto.new verification_string = '' crypto.verify(signature, {signed_text: signed_content}) do |sig| verification_string = sig.to_s end expect(message.to_s).to include('Find the signatures attached.') expect(message.attachments.size).to eql(1) expect(message.attachments.first.filename).to eql('example_key.txt.sig') expect(verification_string).to include('Good signature from D06350440F759BD3') teardown_list_and_mailer(list) end it 'x-list-key with arbitrary email-sub-string' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-KEYs: der@ex" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06/) expect(message.to_s.scan(/^pub /).size).to eql(1) teardown_list_and_mailer(list) end it 'x-list-key with correctly prefixed email-sub-string' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-KEYs: @schleuder" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06/) expect(message.to_s.scan(/^pub /).size).to eql(1) teardown_list_and_mailer(list) end it 'x-list-key with prefixed fingerprint' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-KEYs: 0x59C71FB38AEE22E091C78259D06350440F759BD3" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06/) expect(message.to_s.scan(/^pub /).size).to eql(1) teardown_list_and_mailer(list) end it 'x-get-key with valid argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-GET-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06/) expect(message.to_s).to include('-----BEGIN PGP PUBLIC KEY') teardown_list_and_mailer(list) end it 'x-get-key with invalid argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-KEY: blabla" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('No match found for') expect(message.to_s).not_to include('-----BEGIN PGP PUBLIC KEY') teardown_list_and_mailer(list) end it 'x-get-key with empty argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-KEY:" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.to_s).to include('Your message resulted in no output') expect(message.to_s).not_to include('-----BEGIN PGP PUBLIC KEY') teardown_list_and_mailer(list) end it 'x-get-logfile with debug level sends non-empty logfile' do list = create(:list) list.update_attribute(:log_level, 'debug') list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-logfile" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.parts.last.body.to_s.lines.size).to be > 1 expect(message.parts.last.body.to_s).to include('Logfile created on') expect(message.parts.last.body.to_s).to include('DEBUG') teardown_list_and_mailer(list) end it 'x-get-logfile with error-level sends empty logfile' do list = create(:list) list.update_attribute(:log_level, 'error') list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-logfile" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(1) expect(message.body.to_s).to include('Logfile created on') teardown_list_and_mailer(list) end it 'x-attach-listkey' do list = create(:list, log_level: 'debug') list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.email mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.email => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) content_body = 'something something list-key' mail.body = "x-list-name: #{list.email}\nX-attach-listkey\n\n#{content_body}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(message.parts.length).to eql(2) expect(message.parts.last.parts.length).to eql(2) expect(message.parts.last.parts.first.body.to_s).to eql(content_body) expect(message.parts.last.parts.last.content_type.to_s).to eql('application/pgp-keys') expect(message.parts.last.parts.last.body.decoded).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 /) expect(message.parts.last.parts.last.body.decoded).to include('-----BEGIN PGP PUBLIC KEY BLOCK-----') expect(message.parts.last.parts.last.body.decoded).to include('mQINBFhGvz0BEADXbbTWo/PStyTznAo/f1UobY0EiVPNKNERvYua2Pnq8BwOQ5bS') teardown_list_and_mailer(list) end it 'x-attach-listkey from Thunderbird with protected headers' do list = create(:list, email: 'testlist@example.net') list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) encrypted_mail = File.read('spec/fixtures/mails/attach-list-key-thunderbird.eml') res = nil begin res = Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup expect(res).to be_nil expect(message.parts.length).to eql(2) expect(message.parts.last.parts.length).to eql(2) expect(message.parts.last.parts.first.decoded).to eql("Hallo\r\n\r\nkurz mal testen, wie ein resend mail, wo zusätzlich der listkey attached\r\nist bei euch so ankommt.\r\n\r\nich habe das gefühl hier ist as broken.\r\n\r\n\r\n\r\n\r\n") expect(message.parts.last.parts.last.content_type.to_s).to eql('application/pgp-keys') expect(message.parts.last.parts.last.body.decoded).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06/) expect(message.parts.last.parts.last.body.decoded).to include('-----BEGIN PGP PUBLIC KEY BLOCK-----') expect(message.parts.last.parts.last.body.decoded).to include('mQINBFhGvz0BEADXbbTWo/PStyTznAo/f1UobY0EiVPNKNERvYua2Pnq8BwOQ5bS') teardown_list_and_mailer(list) end it 'x-get-version' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-version" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(1) expect(message.first_plaintext_part.body.to_s).to eql(Schleuder::VERSION) teardown_list_and_mailer(list) end it 'x-get-version with delivery disabled' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true, false) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-get-version" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s.lines.size).to eql(1) expect(message.first_plaintext_part.body.to_s).to eql(Schleuder::VERSION) teardown_list_and_mailer(list) end it 'x-list-keys without arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-keys" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.first_plaintext_part.body.to_s.lines.size).to eql(16) expect(message.first_plaintext_part.body.to_s).to include('59C71FB38AEE22E091C78259D06350440F759BD3') expect(message.first_plaintext_part.body.to_s).to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(message.first_plaintext_part.body.to_s).to include('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') teardown_list_and_mailer(list) end it 'x-list-keys with one argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-keys schleuder2" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.first_plaintext_part.body.to_s.lines.size).to eql(4) expect(message.first_plaintext_part.body.to_s).not_to include('59C71FB38AEE22E091C78259D06350440F759BD3') expect(message.first_plaintext_part.body.to_s).to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(message.first_plaintext_part.body.to_s).not_to include('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') teardown_list_and_mailer(list) end it 'x-list-keys with two arguments' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-keys schleuder2 bla" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.first_plaintext_part.body.to_s.lines.size).to eql(10) expect(message.first_plaintext_part.body.to_s).not_to include('59C71FB38AEE22E091C78259D06350440F759BD3') expect(message.first_plaintext_part.body.to_s).to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(message.first_plaintext_part.body.to_s).to include('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') teardown_list_and_mailer(list) end context 'with broken utf8 in key' do it 'x-list-keys works' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/example_key.txt')) list.import_key(File.read('spec/fixtures/broken_utf8_uid_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-list-keys" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.first_plaintext_part.body.to_s.lines.size).to eql(16) expect(message.first_plaintext_part.body.to_s).to include('59C71FB38AEE22E091C78259D06350440F759BD3') expect(message.first_plaintext_part.body.to_s).to include('3102B29989BEE703AE5ED62E1242F6E13D8EBE4A') teardown_list_and_mailer(list) end it 'x-add-key with inline key-material' do list = create(:list, keywords_admin_notify: []) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list_keys_num = list.keys.size ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) keymaterial = File.read('spec/fixtures/broken_utf8_uid_key.txt') mail.body = "x-list-name: #{list.email}\nX-ADD-KEY:\n\n#{keymaterial}" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(list.keys.size).to eql(list_keys_num + 1) expect(message.to).to eql(['schleuder@example.org']) expect(message.first_plaintext_part.body.to_s).to eql("This key was newly added:\n0x3102B29989BEE703AE5ED62E1242F6E13D8EBE4A info@buendnis-gegen-rechts.ch 2003-01-21\n") teardown_list_and_mailer(list) end it 'x-get-key with valid argument' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) list.import_key(File.read('spec/fixtures/broken_utf8_uid_key.txt')) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nX-GET-KEY: 0x3102B29989BEE703AE5ED62E1242F6E13D8EBE4A" mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.to_s).to match(/pub 1024D\/3102B29989BEE703AE5ED62E1242F6E13D8EBE4A 2003-01-21/) expect(message.to_s).to include('-----BEGIN PGP PUBLIC KEY') teardown_list_and_mailer(list) end end end schleuder-5.0.1/spec/schleuder/integration/protected_headers_spec.rb000066400000000000000000000116661502127241700257640ustar00rootroot00000000000000require 'spec_helper' describe 'protected subject' do it 'is not leaked' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.read('spec/fixtures/mails/protected-headers.eml') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first expect(raw.subject).to eql('Encrypted Message') teardown_list_and_mailer(list) end it 'is included in mime-headers' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.read('spec/fixtures/mails/protected-headers.eml') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(raw.to_s).not_to match('Re: the real subject') expect(message.subject).to eql('Re: the real subject') expect(message.content_type_parameters['protected-headers']).to eql('v1') teardown_list_and_mailer(list) end it 'is included as mime-part in body' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.read('spec/fixtures/mails/protected-headers.eml') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.parts[1].body.to_s).to eql("Subject: Re: the real subject\n") teardown_list_and_mailer(list) end it "don't block request-messages" do list = create(:list, email: 'something@example.org') list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.read('spec/fixtures/mails/protected-headers-request.eml') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.body.to_s).to include('59C71FB38AEE22E091C78259D06350440F759BD3') teardown_list_and_mailer(list) end it 'works with mutt protected headers' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.read('spec/fixtures/mails/mutt_protected_headers.txt') mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.email) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.parts[1].body.to_s).to eql("Subject: x\n") expect(message.parts[2].body.to_s).to eql("test\n") teardown_list_and_mailer(list) end it 'recognizes keywords in mails with protected headers and empty subject' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = "x-list-name: #{list.email}\nx-list-keys" protected_headers = Mail::Part.new do body 'Subject: protected' content_type 'text/rfc822-headers; protected-headers=v1' end mail.add_part protected_headers mail.deliver encrypted_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear begin Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address) rescue SystemExit end raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup expect(message.first_plaintext_part.body.to_s).to include('59C71FB38AEE22E091C78259D06350440F759BD3') expect(message.first_plaintext_part.body.to_s).to_not include("Your message didn't contain any keywords, thus there was nothing to do.") teardown_list_and_mailer(list) end end schleuder-5.0.1/spec/schleuder/integration/receive_bounce_spec.rb000066400000000000000000000007111502127241700252420ustar00rootroot00000000000000require 'spec_helper' describe 'a bounce message is received' do it 'from bounce example' do start_smtp_daemon list = create(:list) list.subscribe('admin@example.org', nil, true) message_path = 'spec/fixtures/mails/bounce.eml' error = run_schleuder(:work, list.email, message_path) mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 stop_smtp_daemon end end schleuder-5.0.1/spec/schleuder/integration/receive_different_charsets_spec.rb000066400000000000000000000013411502127241700276310ustar00rootroot00000000000000require 'spec_helper' describe 'user sends emails with different charsets' do Dir['spec/fixtures/mails/charset_mails/*.eml'].each do |f| it "works with #{File.basename(f, '.eml')}" do start_smtp_daemon list = create(:list) list.subscribe('admin@example.org', nil, true) # Clean any LANG from env as this is usually the case for MUAs # https://0xacab.org/schleuder/schleuder/issues/409 with_env(ENV.delete_if {|key, value| key =~ /LANG/ || key =~ /LC/ }) do error = run_schleuder(:work, list.email, f) mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 end stop_smtp_daemon end end end schleuder-5.0.1/spec/schleuder/integration/send_encrypted_spec.rb000066400000000000000000000011701502127241700252730ustar00rootroot00000000000000require 'spec_helper' describe 'user sends an encrypted message' do [ 'encrypted-inline', 'encrypted-mime', 'encrypted+signed-inline', 'encrypted+signed-mime', ].each do |t| it "from thunderbird being #{t}" do start_smtp_daemon list = create(:list) list.subscribe('admin@example.org', nil, true) message_path = "spec/fixtures/mails/#{t}/thunderbird.eml" error = run_schleuder(:work, list.email, message_path) mails = Dir.glob("#{smtp_daemon_outputdir}/mail-*") expect(error).to be_empty expect(mails.size).to eq 1 stop_smtp_daemon end end end schleuder-5.0.1/spec/schleuder/integration/send_plain_spec.rb000066400000000000000000000015341502127241700244050ustar00rootroot00000000000000require 'spec_helper' describe 'user sends a plain text message' do [ 'plain', 'signed-inline', 'signed-mime', ].each do |t| it "from thunderbird being #{t}" do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/openpgpkey_52507B0163A8D9F0094FFE03B1A36F08069E55DE.asc')) mail = Mail.read("spec/fixtures/mails/#{t}/thunderbird.eml") error = nil begin Schleuder::Runner.new().run(mail.to_s, list.email) rescue SystemExit => exc error = exc end mails = Mail::TestMailer.deliveries expect(error).to be_nil expect(mails.size).to eq 1 content = mails.first.parts[0].parts[1].to_s expect(content).not_to include('-----BEGIN PGP SIGNATURE-----') end end end schleuder-5.0.1/spec/schleuder/runner_spec.rb000066400000000000000000000634651502127241700212720ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::Runner do describe '#run' do context 'with a plain text message' do it 'delivers the incoming message' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') error = Schleuder::Runner.new().run(mail, list.email) expect(Mail::TestMailer.deliveries.length).to eq 1 expect(error).to be_blank teardown_list_and_mailer(list) end it 'has the correct headerlines' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.to).to eq ['admin@example.org'] expect(message.header.to_s.scan('admin@example.org').size).to eq 1 expect(message.from).to eq [list.email] teardown_list_and_mailer(list) end it 'contains the specified pseudoheaders in the correct order' do list = create(:list, send_encrypted_only: false, headers_to_meta: ['from', 'sig']) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first content_part = message.parts.first separator = '------------------------------------------------------------------------------' pseudoheaders = "From: Nina Siessegger \nSig: Unsigned\n#{separator}\n" expect(content_part.parts.first.body).to include(pseudoheaders) expect(content_part.parts.first.body).not_to include('To:') expect(content_part.parts.first.body).not_to include('Enc:') expect(content_part.parts.first.body).not_to include('Date:') teardown_list_and_mailer(list) end it "doesn't have unwanted headerlines from the original message" do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.to).to eq ['admin@example.org'] expect(message.header.to_s.scan('zeromail').size).to eq 0 expect(message.header.to_s.scan('nna.local').size).to eq 0 expect(message.header.to_s.scan('80.187.107.60').size).to eq 0 expect(message.header.to_s.scan('User-Agent:').size).to eq 0 teardown_list_and_mailer(list) end it "doesn't leak the Message-Id as configured" do list = create(:list, send_encrypted_only: false, keep_msgid: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.header.to_s.scan('8db04406-e2ab-fd06-d4c5-c19b5765c52b@web.de').size).to eq 0 teardown_list_and_mailer(list) end it 'does keep the Message-Id as configured' do list = create(:list, send_encrypted_only: false, keep_msgid: true) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.header.to_s.scan('8db04406-e2ab-fd06-d4c5-c19b5765c52b@web.de').size).to eq 1 teardown_list_and_mailer(list) end it 'contains the Autocrypt header if include_autocrypt_header is set to true' do list = create( :list, send_encrypted_only: false, include_autocrypt_header: true, ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first keydata = list.key_minimal_base64_encoded.gsub(/(.{78})/, '\1 ') expect(message.header['Autocrypt'].to_s).to eq("addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata}") teardown_list_and_mailer(list) end it 'does not contain the Autocrypt header if include_autocrypt_header is set to false' do list = create( :list, send_encrypted_only: false, include_autocrypt_header: false, ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.header.to_s).to_not include('Autocrypt') teardown_list_and_mailer(list) end it 'contains the list headers if include_list_headers is set to true' do list = create( :list, email: 'superlist@example.org', send_encrypted_only: false, include_list_headers: true, ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.header['List-Id'].to_s).to eq '' expect(message.header['List-Owner'].to_s).to eq " (Use list's public key)" expect(message.header['List-Help'].to_s).to eq '' teardown_list_and_mailer(list) end it 'contains the open pgp header if include_openpgp_header is set to true' do list = create( :list, send_encrypted_only: false, include_openpgp_header: true, ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.header['Openpgp'].to_s).to include list.fingerprint teardown_list_and_mailer(list) end it 'does not deliver content if send_encrypted_only is set to true' do list = create(:list, send_encrypted_only: true) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.body).to eq '' expect(message.parts.first.body.to_s).to include 'You missed an email from '\ "#{list.email} because your subscription isn't associated with a "\ '(usable) OpenPGP key. Please fix this.' teardown_list_and_mailer(list) end it 'includes the internal_footer' do list = create( :list, send_encrypted_only: false, internal_footer: "-- \nfor our eyes only!" ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.parts.first.parts.last.body.to_s).to eql(list.internal_footer) teardown_list_and_mailer(list) end it 'does not include the public_footer' do public_footer = "-- \nsomething public blabla" list = create( :list, send_encrypted_only: false, internal_footer: "-- \nfor our eyes only!", public_footer: public_footer ) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.parts.first.to_s).not_to include(list.public_footer) teardown_list_and_mailer(list) end end context 'mails not encrypted to the list key' do it 'handles a mail which was encrypted to an absent key and returns DecryptionFailed error' do list = create( :list, send_encrypted_only: false ) list.subscribe('schleuder@example.org', nil, true) mail = File.read('spec/fixtures/mails/encrypted-to-absent-key.txt') result = Schleuder::Runner.new().run(mail, list.email) expect(result.class).to eql(Schleuder::Errors::DecryptionFailed) teardown_list_and_mailer(list) end it 'handles a mail which was encrypted to a passphrase and returns DecryptionFailed error' do list = create( :list, send_encrypted_only: false ) list.subscribe('schleuder@example.org', nil, true) mail = File.read('spec/fixtures/mails/encrypted-to-passphrase.txt') result = Schleuder::Runner.new().run(mail, list.email) expect(result.class).to eql(Schleuder::Errors::DecryptionFailed) teardown_list_and_mailer(list) end it 'handles a mail containing PGP-garbage and returns DecryptionFailed error' do list = create( :list, send_encrypted_only: false ) list.subscribe('schleuder@example.org', nil, true) mail = File.read('spec/fixtures/mails/containing-pgp-garbage.txt') result = Schleuder::Runner.new().run(mail, list.email) expect(result.class).to eql(Schleuder::Errors::DecryptionFailed) teardown_list_and_mailer(list) end end it "delivers a signed error message if a subscription's key is expired on a encrypted-only list" do list = create(:list, send_encrypted_only: true) list.subscribe('admin@example.org', nil, true, false) list.subscribe('expired@example.org', '98769E8A1091F36BD88403ECF71A3F8412D83889') key = File.read('spec/fixtures/expired_key.txt') list.import_key(key) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first verified = message.verify signature_fingerprints = verified.signatures.map(&:fpr) expect(Mail::TestMailer.deliveries.size).to eq 1 expect(message.to).to include('expired@example.org') expect(message.first_plaintext_part.decoded).to include('You missed an email from ') expect(signature_fingerprints).to eq([list.fingerprint]) teardown_list_and_mailer(list) end it "delivers a signed error message if a subscription's key is not available on a encrypted-only list" do list = create(:list, send_encrypted_only: true) list.subscribe('admin@example.org', 'AAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDEEEEEE', true) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first expect(message.to).to eq ['admin@example.org'] expect(message.first_plaintext_part.decoded).to include("You missed an email from #{list.email} ") teardown_list_and_mailer(list) end it 'injects pseudoheaders appropriately into an unsigned thunderbird-multipart/alternative-message' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/multipart-alternative/thunderbird-multi-alt-unsigned.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first content_part = message.parts.first expect(message.to).to eq ['admin@example.org'] expect(content_part.mime_type).to eql('multipart/mixed') expect(content_part.body).to be_blank expect(content_part.parts.size).to eql(2) expect(content_part.parts.first.mime_type).to eql('text/plain') expect(content_part.parts.first.body).to include('From: paz ') expect(content_part.parts.last.mime_type).to eql('multipart/alternative') teardown_list_and_mailer(list) end it 'injects pseudoheaders appropriately into a signed multipart/alternative-message (thunderbird+enigmail-1.9) ' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/multipart-alternative/thunderbird-multi-alt-signed.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first content_part = message.parts.first expect(message.to).to eq ['admin@example.org'] expect(content_part.mime_type).to eql('multipart/mixed') expect(content_part.body).to be_blank expect(content_part.parts.size).to eql(2) expect(content_part.parts.first.mime_type).to eql('text/plain') expect(content_part.parts.first.body).to include('From: paz ') expect(content_part.parts.last.mime_type).to eql('multipart/mixed') expect(content_part.parts.last.parts.size).to eql(1) expect(content_part.parts.last.parts.first.mime_type).to eql('multipart/alternative') teardown_list_and_mailer(list) end context 'Quoted-Printable encoding' do it 'is handled properly in cleartext emails' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/qp-encoding-clear.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first content_part = message.parts.first if content_part.parts.last.content_transfer_encoding == 'quoted-printable' expect(content_part.parts.last.encoded).to include('bug=3D86') expect(content_part.parts.last.encoded).not_to include('bug=86') else expect(content_part.parts.last.encoded).not_to include('bug=3D86') end expect(content_part.parts.last.decoded).to include('bug=86') teardown_list_and_mailer(list) end it 'is handled properly in encrypted+signed emails' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = File.read('spec/fixtures/mails/qp-encoding-encrypted+signed.eml') Schleuder::Runner.new().run(mail, list.email) raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.email, list).setup content_part = message.parts.last.first_plaintext_part expect(content_part.decoded).to include('bug=86') teardown_list_and_mailer(list) end it 'is handled properly in encrypted emails' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/qp-encoding-encrypted.eml') Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first content_part = message.parts.first if content_part.parts.last.content_transfer_encoding == 'quoted-printable' expect(content_part.parts.last.encoded).to include('bug=3D86') expect(content_part.parts.last.encoded).not_to include('bug=86') else expect(content_part.parts.last.encoded).not_to include('bug=3D86') end expect(content_part.parts.last.decoded).to include('bug=86') teardown_list_and_mailer(list) end end it 'does not throw an error on emails with large first mime-part' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = File.read('spec/fixtures/mails/big_first_mime_part.txt') mail.deliver message = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(message.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end it 'does not throw an error on emails that contain other gpg keywords' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = File.read('spec/fixtures/mails/mail_with_pgp_boundaries_in_body.txt') mail.deliver message = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(message.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end it 'does not throw an error on emails with an attached pgp key as application/octet-stream' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: true, sign_as: list.admins.first.fingerprint } mail.gpg(gpg_opts) mail.body = 'See attachment' mail.attachments['251F2412.asc'] = { :content_type => '"application/octet-stream"; name="251F2412.asc"', :content_transfer_encoding => '7bit', :content => File.read('spec/fixtures/bla_foo_key.txt') } mail.deliver message = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(message.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end it 'does not throw an error on encrypted but unsigned emails that contain a forwarded encrypted email' do list = create(:list) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) ENV['GNUPGHOME'] = list.listdir mail = Mail.new mail.to = list.request_address mail.from = list.admins.first.email gpg_opts = { encrypt: true, keys: {list.request_address => list.fingerprint}, sign: false, } mail.gpg(gpg_opts) mail.body = "Hi\n\nI'll forward you this email, have a look at it!\n\n#{File.read('spec/fixtures/mails/encrypted-mime/thunderbird.eml')}" mail.deliver message = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear output = process_mail(message.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end it 'does not throw an error on emails with broken utf-8' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) mail = File.read('spec/fixtures/mails/broken_utf8_charset.eml') # From mail 2.7.0 this is handled correctly # See #334 for background if Gem::Version.new(Mail::VERSION.version) < Gem::Version.new('2.7.0') expect{ Schleuder::Runner.new().run(mail, list.email) }.to raise_error(ArgumentError) else Schleuder::Runner.new().run(mail, list.email) message = Mail::TestMailer.deliveries.first output = process_mail(message.to_s, list.email) expect(output).to be nil end teardown_list_and_mailer(list) end context 'with bounces_drop_all set to true' do it 'drops all bounces when bounces_notify_admins is set to false' do # receive_encrypted_only = true to trigger a bounce list = create(:list, receive_encrypted_only: true, bounces_drop_all: true, bounces_notify_admins: false) list.subscribe('admin@example.org', nil, true) list.subscribe('notadmin@example.org', nil, false) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') error = Schleuder::Runner.new().run(mail, list.email) expect(Mail::TestMailer.deliveries.length).to eq 0 expect(error).to be_blank teardown_list_and_mailer(list) end it 'notifies admins about bounces when bounces_notify_admins is set to true' do # receive_encrypted_only = true to trigger a bounce list = create(:list, receive_encrypted_only: true, bounces_drop_all: true, bounces_notify_admins: true) list.subscribe('admin@example.org', nil, true) list.subscribe('notadmin@example.org', nil, false) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') error = Schleuder::Runner.new().run(mail, list.email) expect(Mail::TestMailer.deliveries.length).to eq 1 expect(Mail::TestMailer.deliveries.first.to).to eq ['admin@example.org'] expect(error).to be_blank teardown_list_and_mailer(list) end end context 'with bounces_drop_all set to false' do it 'bounces and does not notify admins if bounces_notify_admins is set to false' do # receive_encrypted_only = true to trigger a bounce list = create(:list, receive_encrypted_only: true, bounces_drop_all: false, bounces_notify_admins: false) list.subscribe('admin@example.org', nil, true) list.subscribe('notadmin@example.org', nil, false) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') error = Schleuder::Runner.new().run(mail, list.email) expect(Mail::TestMailer.deliveries.length).to eq 0 expect(error.to_s).to include(I18n.t('errors.message_unencrypted')) teardown_list_and_mailer(list) end it 'bounces and notifies admins about bounces when bounces_notify_admins is set to true' do # receive_encrypted_only = true to trigger a bounce list = create(:list, receive_encrypted_only: true, bounces_drop_all: false, bounces_notify_admins: true) list.subscribe('admin@example.org', nil, true) list.subscribe('notadmin@example.org', nil, false) mail = File.read('spec/fixtures/mails/plain/thunderbird.eml') error = Schleuder::Runner.new().run(mail, list.email) expect(Mail::TestMailer.deliveries.length).to eq 1 expect(Mail::TestMailer.deliveries.first.to).to eq ['admin@example.org'] expect(error.to_s).to include(I18n.t('errors.message_unencrypted')) teardown_list_and_mailer(list) end end end context 'after keyword parsing' do it 'falls back to default charset per RFC if none is set' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) # manually build a specific mail structure that comes without a charset mail = Mail.new mail.from = 'admin@example.org' mail.to = list.request_address ENV['GNUPGHOME'] = list.listdir cipher_data = GPGME::Data.new GPGME::Ctx.new({armor: true}) do |ctx| ctx.add_signer(*GPGME::Key.find(:secret, '59C71FB38AEE22E091C78259D06350440F759BD3', :sign)) ctx.encrypt_sign( GPGME::Key.find(:public, list.fingerprint, :encrypt), GPGME::Data.new("Content-Type: text/plain\n\nNur ein test\n"), cipher_data, 0 ) cipher_data.seek(0) end mail.content_type "multipart/encrypted; boundary=\"#{mail.boundary}\"; protocol=\"application/pgp-encrypted\"" ver_part = Mail::Part.new do body 'Version: 1' content_type 'application/pgp-encrypted' end mail.add_part ver_part enc_part = Mail::Part.new do body cipher_data.to_s content_type 'application/octet-stream' end mail.add_part enc_part output = process_mail(mail.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end it 'falling back works also with non-ascii content' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) # manually build a specific mail structure that comes without a charset mail = Mail.new mail.from = 'admin@example.org' mail.to = list.request_address ENV['GNUPGHOME'] = list.listdir cipher_data = GPGME::Data.new GPGME::Ctx.new({armor: true}) do |ctx| ctx.add_signer(*GPGME::Key.find(:secret, '59C71FB38AEE22E091C78259D06350440F759BD3', :sign)) ctx.encrypt_sign( GPGME::Key.find(:public, list.fingerprint, :encrypt), GPGME::Data.new("Content-Type: text/plain\n\nNür ein test\n"), cipher_data, 0 ) cipher_data.seek(0) end mail.content_type "multipart/encrypted; boundary=\"#{mail.boundary}\"; protocol=\"application/pgp-encrypted\"" ver_part = Mail::Part.new do body 'Version: 1' content_type 'application/pgp-encrypted' end mail.add_part ver_part enc_part = Mail::Part.new do body cipher_data.to_s content_type 'application/octet-stream' end mail.add_part enc_part output = process_mail(mail.to_s, list.email) expect(output).to be nil teardown_list_and_mailer(list) end end end schleuder-5.0.1/spec/schleuder/unit/000077500000000000000000000000001502127241700173635ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder/unit/conf_spec.rb000066400000000000000000000017111502127241700216470ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::Conf do it 'reads ERB code in config files' do # Suppress warnings about already defined constants # if using "load" further below verbose_orig = $VERBOSE $VERBOSE = nil # Define constants val_old = 'val_old' # Check if env var is set if not ENV['SCHLEUDER_DB_PATH'].nil? val_old = ENV['SCHLEUDER_DB_PATH'] end # Set env var, reload the config and check whether the correct value # is returned val_test = 'SCHLEUDER_ERB_TEST' ENV['SCHLEUDER_DB_PATH'] = val_test load 'schleuder/conf.rb' expect(Schleuder::Conf.database['database']).to eql(val_test) # Reset the env var ENV['SCHLEUDER_DB_PATH'] = nil # Set the env var to the original value if val_old != 'val_old' ENV['SCHLEUDER_DB_PATH'] = val_old end load 'schleuder/conf.rb' # Set verbose level to original value $VERBOSE = $verbose_orig end end schleuder-5.0.1/spec/schleuder/unit/filters_runner_spec.rb000066400000000000000000000135301502127241700237650ustar00rootroot00000000000000require 'spec_helper' module Schleuder::Filters def self.dummy(list, mail) nil end def self.stop(list, mail) nil end end describe Schleuder::Filters::Runner do let(:list) do # setup the list with an admin that can be notified list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) list end let(:pre_filters) { Schleuder::Filters::Runner.new(list, 'pre') } let(:post_filters){ Schleuder::Filters::Runner.new(list, 'post') } it { expect(pre_filters).to respond_to :run } context '#run' do it 'runs the filters' do mail = Mail.new expect(Schleuder::Filters).to receive(:dummy).once expect(Schleuder::Filters).to_not receive(:stop) expect(pre_filters).to receive(:filters).and_return(['dummy']) expect(pre_filters.run(mail)).to be_nil end it 'stops on a StandardError and returns error' do mail = Mail.new error = StandardError.new expect(Schleuder::Filters).to_not receive(:dummy) expect(Schleuder::Filters).to receive(:stop).once { error } expect(pre_filters).to receive(:filters).and_return(['stop', 'dummy']) expect(pre_filters.run(mail)).to eql(error) expect(Mail::TestMailer.deliveries.first).to be_nil end it 'stops on a StandardError and returns error even with bounces_drop_all' do mail = Mail.new error = StandardError.new pre_filters.list.bounces_drop_all = true expect(Schleuder::Filters).to_not receive(:dummy) expect(Schleuder::Filters).to receive(:stop).once { error } expect(pre_filters).to receive(:filters).and_return(['stop', 'dummy']) expect(pre_filters.run(mail)).to_not be_nil expect(Mail::TestMailer.deliveries.first).to be_nil end it 'stops on a StandardError and returns error even on headers match' do mail = Mail.new error = StandardError.new mail['X-SPAM-FLAG'] = 'TRUE' expect(Schleuder::Filters).to_not receive(:dummy) expect(Schleuder::Filters).to receive(:stop).once { error } expect(pre_filters).to receive(:filters).and_return(['stop', 'dummy']) expect(pre_filters.run(mail)).to_not be_nil expect(Mail::TestMailer.deliveries.first).to be_nil end end context 'loading filters' do it 'loads filters from built-in filters_dir sorts them' do Schleuder::Conf.instance.config['filters_dir'] = File.join(Dir.pwd, 'spec/fixtures/no_filters') expect(pre_filters.filters).to eq [ 'forward_bounce_to_admins', 'forward_all_incoming_to_admins', 'send_key', 'fix_exchange_messages', 'strip_html_from_alternative', 'key_auto_import_from_autocrypt_header' ] expect(post_filters.filters).to eq [ 'request', 'max_message_size', 'forward_to_owner', 'key_auto_import_from_attachments', 'receive_admin_only', 'receive_authenticated_only', 'receive_signed_only', 'receive_encrypted_only', 'receive_from_subscribed_emailaddresses_only', 'strip_html_from_alternative_if_keywords_present' ] end it 'loads custom filters from filters_dir and sorts them in, ignores filter not following convention' do Schleuder::Conf.instance.config['filters_dir'] = File.join(Dir.pwd, 'spec/fixtures/filters') expect(pre_filters.filters).to eq [ 'forward_bounce_to_admins', 'forward_all_incoming_to_admins', 'example', 'send_key', 'fix_exchange_messages', 'strip_html_from_alternative', 'key_auto_import_from_autocrypt_header' ] expect(post_filters.filters).to eq [ 'request', 'max_message_size', 'forward_to_owner', 'key_auto_import_from_attachments', 'receive_admin_only', 'receive_authenticated_only', 'receive_signed_only', 'receive_encrypted_only', 'post_example', 'receive_from_subscribed_emailaddresses_only', 'strip_html_from_alternative_if_keywords_present' ] end it 'loads custom filters from filters_dir and sorts them in with missing dir' do Schleuder::Conf.instance.config['filters_dir'] = File.join(Dir.pwd, 'spec/fixtures/filters_without_pre') expect(pre_filters.filters).to eq [ 'forward_bounce_to_admins', 'forward_all_incoming_to_admins', 'send_key', 'fix_exchange_messages', 'strip_html_from_alternative', 'key_auto_import_from_autocrypt_header' ] expect(post_filters.filters).to eq [ 'post_example', 'request', 'max_message_size', 'forward_to_owner', 'key_auto_import_from_attachments', 'receive_admin_only', 'receive_authenticated_only', 'receive_signed_only', 'receive_encrypted_only', 'receive_from_subscribed_emailaddresses_only', 'strip_html_from_alternative_if_keywords_present' ] end it 'loads custom filters from filters_dir even with non-2-digit priority' do Schleuder::Conf.instance.config['filters_dir'] = File.join(Dir.pwd, 'spec/fixtures/more_filters') expect(pre_filters.filters).to eq [ 'early_example', 'forward_bounce_to_admins', 'forward_all_incoming_to_admins', 'example', 'send_key', 'fix_exchange_messages', 'strip_html_from_alternative', 'key_auto_import_from_autocrypt_header', 'late_example' ] expect(post_filters.filters).to eq [ 'request', 'max_message_size', 'forward_to_owner', 'key_auto_import_from_attachments', 'receive_admin_only', 'receive_authenticated_only', 'receive_signed_only', 'receive_encrypted_only', 'receive_from_subscribed_emailaddresses_only', 'strip_html_from_alternative_if_keywords_present', ] end end end schleuder-5.0.1/spec/schleuder/unit/filters_spec.rb000066400000000000000000000450011502127241700223720ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::Filters do before do # Make sure we have the filters loaded, as they will be loaded lazily within the code. list = create(:list) Schleuder::Filters::Runner.new(list, 'pre').filters Schleuder::Filters::Runner.new(list, 'post').filters end context '.fix_exchange_messages' do it 'fixes pgp/mime-messages that were mangled by Exchange' do message = Mail.read('spec/fixtures/mails/exchange.eml') Schleuder::Filters.fix_exchange_messages(nil, message) expect(message[:content_type].content_type).to eql('multipart/encrypted') end it 'works with a text/plain message' do message = Mail.read('spec/fixtures/mails/exchange_no_parts.eml') Schleuder::Filters.fix_exchange_messages(nil, message) expect(message[:content_type].content_type).to eql('text/plain') end end context '.strip_html_from_alternative' do it 'strips HTML-part from multipart/alternative-message that contains ascii-armored PGP-data' do list = create(:list) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'outside@example.org' content = encrypt_string(list, 'blabla') mail.text_part = content mail.html_part = "

#{content}

" mail.subject = 'test' Schleuder::Filters.strip_html_from_alternative(list, mail) expect(mail[:content_type].content_type).to eql('multipart/mixed') expect(mail.parts.size).to be(1) expect(mail.parts.first[:content_type].content_type).to eql('text/plain') expect(mail.dynamic_pseudoheaders).to include("Note: This message included an alternating HTML-part that contained\n PGP-data. The HTML-part was removed to enable parsing the message more\n properly.") end it 'does NOT strip HTML-part from multipart/alternative-message that does NOT contain ascii-armored PGP-data' do mail = Mail.new mail.to = 'schleuder@example.org' mail.from = 'outside@example.org' content = 'blabla' mail.text_part = content mail.html_part = "

#{content}

" mail.subject = 'test' Schleuder::Filters.strip_html_from_alternative(nil, mail) expect(mail[:content_type].content_type).to eql('multipart/alternative') expect(mail.parts.size).to be(2) expect(mail.parts.first[:content_type].content_type).to eql('text/plain') expect(mail.parts.last[:content_type].content_type).to eql('text/html') expect(mail.dynamic_pseudoheaders).not_to include('Note: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly.') end it 'does not choke on nor change a message without Content-Type-header' do mail = Mail.new mail.to = 'schleuder@example.org' mail.from = 'outside@example.org' mail.body = 'blabla' mail.subject = 'test' Schleuder::Filters.strip_html_from_alternative(nil, mail) expect(mail[:content_type]).to be_nil expect(mail.parts.size).to be(0) expect(mail.dynamic_pseudoheaders).not_to include('Note: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly.') end end context '.strip_html_from_alternative_if_keywords_present' do it 'strips HTML-part from multipart/alternative-message that contains keywords' do list = create(:list) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'outside@example.org' mail.text_part = content = 'x-resend: someone@example.org\n\nblabla' mail.html_part = '

x-resend: someone@example.org

blabla

' mail.subject = 'test' mail.to_s Schleuder::Filters.strip_html_from_alternative_if_keywords_present(list, mail) expect(mail[:content_type].content_type).to eql('multipart/mixed') expect(mail.parts.size).to be(1) expect(mail.parts.first[:content_type].content_type).to eql('text/plain') expect(mail.dynamic_pseudoheaders).to include("Note: This message included keywords and an alternating HTML-part. The\n HTML-part was removed to prevent the disclosure of these keywords to third\n parties.") end it 'strips related-part from encapsulated multipart/alternative-part that contains keywords' do list = create(:list) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'outside@example.org' content_plain = "x-resend: someone@example.org\n\nblabla" content_html = '

x-resend: someone@example.org

blabla

' # this makes the message a multipart/mixed mail.part :content_type => 'multipart/alternative' do |part_alter| part_alter.part :content_type => 'text/plain', :body => content_plain part_alter.part :content_type => 'multipart/related' do |part_related| part_related.part :content_type => 'text/html', :body => content_html part_related.part :content_type => 'image/png' end end mail.subject = 'test' mail.to_s Schleuder::Filters.strip_html_from_alternative_if_keywords_present(list, mail) expect(mail.parts.first[:content_type].content_type).to eql('multipart/mixed') expect(mail.parts.first.parts.size).to be(1) expect(mail.parts.first.parts.first[:content_type].content_type).to eql('text/plain') expect(mail.parts.first.dynamic_pseudoheaders).to include("Note: This message included keywords and an alternating HTML-part. The\n HTML-part was removed to prevent the disclosure of these keywords to third\n parties.") end it 'does NOT strip HTML-part from multipart/alternative-message that does NOT contain keywords' do list = create(:list) mail = Mail.new mail.list = list mail.to = 'schleuder@example.org' mail.from = 'outside@example.org' mail.text_part = content = 'Hello someone@example.org,\n\nblabla' mail.html_part = '

Hello someone@example.org,

blabla

' mail.subject = 'test' Schleuder::Filters.strip_html_from_alternative_if_keywords_present(list, mail) expect(mail[:content_type].content_type).to eql('multipart/alternative') expect(mail.parts.size).to be(2) expect(mail.parts.first[:content_type].content_type).to eql('text/plain') expect(mail.parts.last[:content_type].content_type).to eql('text/html') expect(mail.dynamic_pseudoheaders).to be_blank end it 'does not choke on nor change a message without Content-Type-header' do mail = Mail.new mail.to = 'schleuder@example.org' mail.from = 'outside@example.org' mail.body = 'blabla' mail.subject = 'test' Schleuder::Filters.strip_html_from_alternative_if_keywords_present(nil, mail) expect(mail[:content_type]).to be_nil expect(mail.parts.size).to be(0) expect(mail.dynamic_pseudoheaders).to be_blank end end context '.receive_from_subscribed_emailaddresses_only' do it 'does not reject a message with a non-subscribed address as From-header if list.receive_from_subscribed_emailaddresses_only is not set' do list = create(:list, receive_from_subscribed_emailaddresses_only: false) list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'outside@example.org' result = Schleuder::Filters.receive_from_subscribed_emailaddresses_only(list, mail) expect(result).to eql(nil) end it 'rejects a message with a non-subscribed address as From-header if list.receive_from_subscribed_emailaddresses_only is set' do list = create(:list, receive_from_subscribed_emailaddresses_only: true) list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'outside@example.org' result = Schleuder::Filters.receive_from_subscribed_emailaddresses_only(list, mail) expect(result).to be_a(Errors::MessageSenderNotSubscribed) end it 'does not reject a message with a subscribed address as From-header if list.receive_from_subscribed_emailaddresses_only is set' do list = create(:list, receive_from_subscribed_emailaddresses_only: true) list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.list = list mail.to = list.email mail.from = list.subscriptions.first.email result = Schleuder::Filters.receive_from_subscribed_emailaddresses_only(list, mail) expect(result).to eql(nil) end it 'does not reject a message with a subscribed address as From-header with different letter case if list.receive_from_subscribed_emailaddresses_only is set' do list = create(:list, receive_from_subscribed_emailaddresses_only: true) list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.list = list mail.to = list.email mail.from = 'AdMin@example.org' result = Schleuder::Filters.receive_from_subscribed_emailaddresses_only(list, mail) expect(result).to eql(nil) end end context('.key_auto_import_from_autocrypt_header') do it('does not import key if sender address does not match key UID, regardless of Autocrypt addr attribute') do keydata_base64 = Base64.encode64(File.read('spec/fixtures/bla_foo_key.txt')) list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = list.email mail.text_part = 'bla' mail.header['Autocrypt'] = "addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata_base64}" list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_autocrypt_header(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).not_to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end it('imports key and reports the change') do keydata_base64 = Base64.encode64(File.read('spec/fixtures/expired_key_extended.txt')) mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) list.import_key(File.read('spec/fixtures/expired_key.txt')) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.header['Autocrypt'] = "addr=#{mail_from}; prefer-encrypt=mutual; keydata=#{keydata_base64}" list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_autocrypt_header(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).to include("This key was updated from this email:\n 0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired:\n 2017-01-20]") end it('imports key and reports no change') do keydata_base64 = File.read('spec/fixtures/schleuder_at_example_public_key_minimal_base64.txt') mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.header['Autocrypt'] = "addr=#{mail_from}; prefer-encrypt=mutual; keydata=#{keydata_base64}" list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_autocrypt_header(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).to include("This key was unchanged from this email:\n 0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06.") end it('imports key and reports new key') do keydata_base64 = Base64.encode64(File.read('spec/fixtures/bla_foo_key.txt')) mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.header['Autocrypt'] = "addr=#{mail_from}; prefer-encrypt=mutual; keydata=#{keydata_base64}" list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_autocrypt_header(list, mail) expect(list.keys.size).to eql(list_keys_num + 1) expect(mail.dynamic_pseudoheaders.join("\n")).to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end it('only imports the one key that matches the sender address if keydata contains more than one key') do tmpdir = Dir.mktmpdir keydata = `gpg --homedir #{tmpdir} --import spec/fixtures/bla_foo_key.txt spec/fixtures/example_key.txt 2>/dev/null ; gpg --homedir #{tmpdir} -a --export` FileUtils.rm_rf(tmpdir) keydata_base64 = Base64.encode64(keydata) mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.header['Autocrypt'] = "addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata_base64}" list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_autocrypt_header(list, mail) expect(list.keys.size).to eql(list_keys_num + 1) expect(mail.dynamic_pseudoheaders.join("\n")).to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end end context('.key_auto_import_from_attachments') do it('does not import key if sender address does not match key UID') do list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = list.email mail.text_part = 'bla' mail.add_file({ filename: 'something.pgp', content: File.read('spec/fixtures/bla_foo_key.txt'), mime_type: 'application/pgp-keys' }) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).not_to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end it('imports key and reports the change') do mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) list.import_key(File.read('spec/fixtures/expired_key.txt')) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.add_file({ filename: 'something.pgp', content: File.read('spec/fixtures/expired_key_extended.txt'), mime_type: 'application/pgp-keys' }) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).to include("This key was updated from this email:\n 0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired:\n 2017-01-20]") end it('imports key and reports no change') do mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.add_file({ filename: 'something.pgp', content: File.read('spec/fixtures/schleuder_at_example_public_key.txt'), mime_type: 'application/pgp-keys' }) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).to include("This key was unchanged from this email:\n 0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06.") end it('imports key and reports new key') do mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.add_file({ filename: 'something.pgp', content: File.read('spec/fixtures/bla_foo_key.txt'), mime_type: 'application/pgp-keys' }) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num + 1) expect(mail.dynamic_pseudoheaders.join("\n")).to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end it('does not import key if attachment has a different content-type than "application/pgp-keys"') do mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.add_file({ filename: 'something.pgp', content: File.read('spec/fixtures/bla_foo_key.txt'), mime_type: 'text/plain' }) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num) expect(mail.dynamic_pseudoheaders.join("\n")).not_to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end it('only imports the one key that matches the sender address if keydata contains more than one key') do tmpdir = Dir.mktmpdir keydata = `gpg --homedir #{tmpdir} --import spec/fixtures/bla_foo_key.txt spec/fixtures/example_key.txt 2>/dev/null ; gpg --homedir #{tmpdir} -a --export` FileUtils.rm_rf(tmpdir) mail_from = 'schleuder ' list = create(:list, key_auto_import_from_email: true) mail = Mail.new mail.list = list mail.to = list.email mail.from = mail_from mail.text_part = 'bla' mail.add_file({filename: 'something.pgp', content: keydata, mime_type: 'application/pgp-keys'}) list_keys_num = list.keys.size Schleuder::Filters.key_auto_import_from_attachments(list, mail) expect(list.keys.size).to eql(list_keys_num + 1) expect(mail.dynamic_pseudoheaders.join("\n")).to include("Note: This key was newly added from this email:\n 0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412") end end end schleuder-5.0.1/spec/schleuder/unit/gpgme_ctx_spec.rb000066400000000000000000000112251502127241700227000ustar00rootroot00000000000000require 'spec_helper' describe GPGME::Ctx do it '#keyimport' do list = create(:list) keymaterial = File.read('spec/fixtures/example_key.txt') expect(list.gpg.keys.map(&:fingerprint)).not_to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect { list.gpg.keyimport(keymaterial) }.to change{ list.gpg.keys.size }.by 1 expect(list.gpg.keys.map(&:fingerprint)).to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end it '#keyimport with unusable data' do list = create(:list) keymaterial = 'blabla' expect(list.gpg.keys.map(&:fingerprint)).not_to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect { list.gpg.keyimport(keymaterial) }.to change{ list.gpg.keys.size }.by 0 expect(list.gpg.keys.map(&:fingerprint)).not_to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end it '#find_keys with prefixed fingerprint' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('0x59C71FB38AEE22E091C78259D06350440F759BD3') expect(keys.size).to eql(1) end it '#find_keys with un-prefixed fingerprint' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('59C71FB38AEE22E091C78259D06350440F759BD3') expect(keys.size).to eql(1) end it '#find_keys with bracketed email-address' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('schleuder ') expect(keys.size).to eql(1) end it '#find_keys with bracketed wrong email-address' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('blabla ') expect(keys.size).to eql(0) end it '#find_keys with un-bracketed email-address' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('schleuder@example.org') expect(keys.size).to eql(1) end it '#find_keys with un-bracketed wrong email-address' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('blabla@example.org') expect(keys.size).to eql(0) end it '#find_keys with correctly marked sub-string' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('@schleuder') expect(keys.size).to eql(2) end it '#find_keys with correctly marked narrower sub-string' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('@schleuder@') expect(keys.size).to eql(1) end it '#find_keys with un-marked sub-string' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys('schleuder') expect(keys.size).to eql(2) end it '#find_keys without argument' do list = create(:list) list.import_key(File.read('spec/fixtures/example_key.txt')) keys = list.gpg.find_keys() expect(keys.size).to eql(2) end it '#normalize_key_identifier with prefixed fingerprint' do list = create(:list) input = list.gpg.normalize_key_identifier('0x59C71FB38AEE22E091C78259D06350440F759BD3') expect(input).to eql('0x59C71FB38AEE22E091C78259D06350440F759BD3') end it '#normalize_key_identifier with un-prefixed fingerprint' do list = create(:list) input = list.gpg.normalize_key_identifier('59C71FB38AEE22E091C78259D06350440F759BD3') expect(input).to eql('0x59C71FB38AEE22E091C78259D06350440F759BD3') end it '#normalize_key_identifier with bracketed email-address' do list = create(:list) input = list.gpg.normalize_key_identifier('bla ') expect(input).to eql('') end it '#normalize_key_identifier with un-bracketed email-address' do list = create(:list) input = list.gpg.normalize_key_identifier('bla@foo') expect(input).to eql('') end it '#normalize_key_identifier with URL' do list = create(:list) input = list.gpg.normalize_key_identifier('http://example.org/foo') expect(input).to eql('http://example.org/foo') end it '#normalize_key_identifier with some string' do list = create(:list) input = list.gpg.normalize_key_identifier('lala') expect(input).to eql('lala') end it '#gpgcli returns correct data types' do list = create(:list) err, out, exitcode = list.gpg.class.gpgcli('--list-keys') expect(err.class).to eql(Array) expect(out.class).to eql(Array) expect(exitcode).to be_a(Numeric) end end schleuder-5.0.1/spec/schleuder/unit/gpgme_key_spec.rb000066400000000000000000000060651502127241700227000ustar00rootroot00000000000000require 'spec_helper' describe GPGME::Key do describe '#minimal' do it 'returns a minimal key' do list = create(:list) list.import_key(File.read('spec/fixtures/schleuder_at_example_public_key.txt')) expect(list.key.minimal()).to eq(File.read('spec/fixtures/schleuder_at_example_public_key_minimal.gpg', mode: 'rb')) end end describe '#summary' do it 'displays the expected basic attributes' do list = create(:list) key = list.key expect(key.summary).to eql('0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06') end it 'displays the expected attributes for an expiring key' do list = create(:list) list.import_key(File.read('spec/fixtures/expiring_key.txt')) key = list.key('421FBF7190640136788593CD9EE9BE5929CACC20') expect(key.summary).to match(/0x421FBF7190640136788593CD9EE9BE5929CACC20 expiringkey@example.org 2017-08-03 \[expire(?:d)?(?:s)?: 2037-07-29\]/) end it 'displays the expected attributes for an expired key' do list = create(:list) list.import_key(File.read('spec/fixtures/expired_key.txt')) key = list.key('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(key.summary).to eql('0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2010-08-14]') end it 'displays the expected attributes for a revoked key' do list = create(:list) list.import_key(File.read('spec/fixtures/revoked_key.txt')) key = list.key('7E783CDE6D1EFE6D2409739C098AC83A4C0028E9') expect(key.summary).to eql('0x7E783CDE6D1EFE6D2409739C098AC83A4C0028E9 paz@nadir.org 2008-09-20 [revoked]') end # gpgme.rb doesn't report missing encryption-capability properly yet. it "displays the expected attributes for a key that's not capable of encryption" do list = create(:list) list.import_key(File.read('spec/fixtures/signonly_key.txt')) key = list.key('B1CD8BB15C2673C6BFD8FA4B70B2CF29E01AD53E') expect(key.summary).to eql('0xB1CD8BB15C2673C6BFD8FA4B70B2CF29E01AD53E signonly@example.org 2017-08-03 [not capable of encryption]') end end describe '.valid_fingerprint?' do context 'valid fingerprints' do ['59C71FB38AEE22E091C78259D06350440F759BD3', '0x59C71FB38AEE22E091C78259D06350440F759BD3', '59C71FB38AEE22E091C78259D0635044', '0x59C71FB38AEE22E091C78259D0635044', ].each do |fp| it "accepts #{fp} as a valid fingerprint" do expect(described_class.valid_fingerprint?(fp)).to be_truthy end end end context 'invalid fingerprints' do ['Z9C71FB38AEE22E091C78259D06350440F759BD3', '59C71FB38AEE22E091C78259D06350440F759BD3A', '59C71FB38AEE22E091C78259D06350440F759BD', '0x59C71FB38AEE22E091C78259D06350440F759B', 'Z9C71FB38AEE22E091C78259D0635044', 'Z9C71FB38AEE22E091C78259D0635044', ].each do |fp| it "rejects #{fp} as an invalid fingerprint" do expect(described_class.valid_fingerprint?(fp)).to be_falsey end end end end end schleuder-5.0.1/spec/schleuder/unit/http_spec.rb000066400000000000000000000005071502127241700217030ustar00rootroot00000000000000require 'spec_helper' describe Http do it 'uses a proxy if one is configured' do proxy_url = 'socks5h://localhost:9050' Conf.instance.config['http_proxy'] = proxy_url http = Http.new('http://localhost/something') expect(http.request.options[:proxy]).to eql(proxy_url) Conf.instance.reload! end end schleuder-5.0.1/spec/schleuder/unit/key_fetcher_spec.rb000066400000000000000000000116241502127241700232160ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::KeyFetcher do context '#fetch' do it 'reports an error if both, vks_keyserver and sks_keyserver, are blank' do Conf.instance.config['vks_keyserver'] = '' Conf.instance.config['sks_keyserver'] = '' list = create(:list) output = KeyFetcher.new(list).fetch('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(output).to eql('Error while fetching data from the internet: No keyserver configured, cannot query anything') Conf.instance.reload! teardown_list_and_mailer(list) end it 'fetches one key by fingerprint from SKS if vks_keyserver is blank' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/search=98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp) Conf.instance.config['vks_keyserver'] = '' list = create(:list) output = KeyFetcher.new(list).fetch('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) Conf.instance.reload! teardown_list_and_mailer(list) end it 'fetches one key by email from SKS if vks_keyserver is blank' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/search=admin%40example.net/).and_return(resp) Conf.instance.config['vks_keyserver'] = '' list = create(:list) output = KeyFetcher.new(list).fetch('admin@example.net') expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) Conf.instance.reload! teardown_list_and_mailer(list) end it 'fetches one key by fingerprint from VKS if vks_keyserver is set' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) teardown_list_and_mailer(list) end it 'fetches one key by email from VKS if vks_keyserver is set' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-email\/admin%40example.net/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('admin@example.net') expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) teardown_list_and_mailer(list) end it 'fetches one key from a good URL' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/example.asc/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('https://localhost/example.asc') expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) teardown_list_and_mailer(list) end it "reports an error from trying to fetch an URL that doesn't exist" do resp = Typhoeus::Response.new(code: 404, body: 'Not Found') Typhoeus.stub(/something/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('https://localhost/something') expect(output).to eql("Error: There's nothing at (404 Not Found).") teardown_list_and_mailer(list) end it 'reports an error from trying to import non-key-material' do resp = Typhoeus::Response.new(code: 200, body: 'blabla') Typhoeus.stub(/example.asc/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('https://localhost/example.asc') expect(output).to eql("Error while importing the fetched data: gpg: no valid OpenPGP data found.\n") teardown_list_and_mailer(list) end it 'reports the returned body content when receiving an unexpected HTTP status from the server' do resp = Typhoeus::Response.new(code: 503, body: 'Internal server error') Typhoeus.stub(/example.asc/).and_return(resp) list = create(:list) output = KeyFetcher.new(list).fetch('https://localhost/example.asc') expect(output).to eql('Error while fetching data from the internet: Internal server error') teardown_list_and_mailer(list) end end end schleuder-5.0.1/spec/schleuder/unit/keyword_handlers/000077500000000000000000000000001502127241700227275ustar00rootroot00000000000000schleuder-5.0.1/spec/schleuder/unit/keyword_handlers/base_spec.rb000066400000000000000000000012771502127241700252070ustar00rootroot00000000000000require 'spec_helper' describe KeywordHandlers::Base do it 'stores mail, list and arguments as instance variables' do mail = Mail.new mail.list = create(:list) arguments = %w[1 2 3] instance = KeywordHandlers::Base.new(mail: mail, arguments: arguments) expect(instance.instance_variable_get('@mail')).to eql(mail) expect(instance.instance_variable_get('@list')).to eql(mail.list) expect(instance.instance_variable_get('@arguments')).to eql(arguments) end it 'provides methods to register keywords' do expect(KeywordHandlers::Base.methods).to include(:handles_list_keyword) expect(KeywordHandlers::Base.methods).to include(:handles_request_keyword) end end schleuder-5.0.1/spec/schleuder/unit/keyword_handlers/key_management_spec.rb000066400000000000000000000271331502127241700272600ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::KeywordHandlers::KeyManagement do it 'registers keywords' do found_keywords = KeywordHandlersRunner::REGISTERED_KEYWORDS.values.map do |hash| hash.select do |keyword, attributes| attributes[:klass] == Schleuder::KeywordHandlers::KeyManagement end.keys end.flatten expect(found_keywords).to eql(%w[add-key delete-key list-keys get-key fetch-key]) end context '.add_key' do it 'imports a key from inline ascii-armored material' do mail = Mail.new mail.list = create(:list) mail.body = File.read('spec/fixtures/example_key.txt') mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports from an inline mix of ascii-armored key and non-key material' do mail = Mail.new mail.list = create(:list) keymaterial1 = File.read('spec/fixtures/example_key.txt') keymaterial2 = File.read('spec/fixtures/bla_foo_key.txt') mail.body = "#{keymaterial1}\nsome text\n#{keymaterial2}\n--\nthis is a signature" mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n\n\nThis key was newly added:\n0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412 bla@foo 2010-03-14\n") expect(mail.list.keys.size).to eql(list_keys.size + 2) end it 'imports a key from attached acsii-armored material' do mail = Mail.new mail.list = create(:list) mail.add_file('spec/fixtures/example_key.txt') mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports a key from attached quoted-printable binary material' do mail = Mail.new mail.list = create(:list) mail.attachments['example_key_binary.pgp'] = { :mime_type => 'application/pgp-keys', :content_transfer_encoding => 'quoted-printable', :content => File.open('spec/fixtures/example_key_binary.pgp', 'rb', &:read) } mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports a key from attached binary material (without specified encoding)' do mail = Mail.new mail.list = create(:list) mail.add_file('spec/fixtures/example_key_binary.pgp') list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports a key from attached explicitly base64-encoded binary material' do mail = Mail.new mail.list = create(:list) mail.attachments['example_key_binary.pgp'] = { :mime_type => 'application/pgp-keys', :content_transfer_encoding => 'base64', :content => Base64.encode64(File.binread('spec/fixtures/example_key_binary.pgp')) } list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports from attached quoted-printable binary key-material (as produced by Mutt 2.0.5)' do encrypted_email = Mail.read('spec/fixtures/mails/mutt-2.0.5-linux-xaddkey-attachment-binary.eml') encrypted_email.list = create(:list, fingerprint: '421C19AF8B9C33B8B62D76EBDB2F7E271D773073') encrypted_email.list.import_key(File.read('spec/fixtures/openpgpkey_421C19AF8B9C33B8B62D76EBDB2F7E271D773073.sec')) mail = encrypted_email.setup mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports from attached quoted-printable ascii-armored key-material' do mail = Mail.new mail.list = create(:list) mail.attachments['example_key.txt'] = { :mime_type => 'application/pgp-keys', :content_transfer_encoding => 'quoted-printable', :content => File.read('spec/fixtures/example_key.txt') } mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'imports from attached quoted-printable key-material (as produced by Thunderbird 115)' do encrypted_email = Mail.read('spec/fixtures/mails/thunderbird-115.2.3-macos-xaddkey-attachment.eml') encrypted_email.list = create(:list, fingerprint: '421C19AF8B9C33B8B62D76EBDB2F7E271D773073') encrypted_email.list.import_key(File.read('spec/fixtures/openpgpkey_421C19AF8B9C33B8B62D76EBDB2F7E271D773073.sec')) mail = encrypted_email.setup mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0x769B651054DB697FEB26E408717574BD0A6591ED paz@schleuder.org 2018-10-16 [expired: 2024-03-16]\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'ignores body if an ascii-armored attachment is present' do mail = Mail.new mail.list = create(:list) mail.body = 'blabla' mail.add_file('spec/fixtures/example_key.txt') mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was newly added:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size + 1) end it 'ignores arguments' do mail = Mail.new mail.list = create(:list) mail.body = 'blabla' mail.to_s list_keys = mail.list.keys key_material = File.read('spec/fixtures/example_key.txt').lines output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: key_material).add_key expect(output).to eql('In the message you sent us, no keys could be found. :(') expect(mail.list.keys.size).to eql(list_keys.size) end it 'updates a key' do mail = Mail.new mail.list = create(:list) mail.body = 'blabla' mail.add_file('spec/fixtures/expired_key_extended.txt') mail.to_s mail.list.import_key(File.read('spec/fixtures/expired_key.txt')) list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql("This key was updated:\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") expect(mail.list.keys.size).to eql(list_keys.size) end it 'rejects garbage' do mail = Mail.new mail.list = create(:list) mail.body = 'blabla' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql('In the message you sent us, no keys could be found. :(') expect(mail.list.keys.size).to eql(list_keys.size) end end context '.delete_key' do it 'deletes a key that distinctly matches the argument' do mail = Mail.new mail.list = create(:list) mail.list.import_key(File.read('spec/fixtures/example_key.txt')) mail.body = 'ignore me' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: ['C4D60F8833789C7CAA44496FD3FFA6613AB10ECE']).delete_key expect(output).to eql("This key was deleted:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n") expect(mail.list.keys.size).to eql(list_keys.size - 1) end it 'deletes multiple keys that each distinctly match one argument' do mail = Mail.new mail.list = create(:list) mail.list.import_key(File.read('spec/fixtures/example_key.txt')) mail.list.import_key(File.read('spec/fixtures/expired_key_extended.txt')) mail.body = 'ignore me' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: ['C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', '98769E8A1091F36BD88403ECF71A3F8412D83889']).delete_key expect(output).to eql("This key was deleted:\n0xC4D60F8833789C7CAA44496FD3FFA6613AB10ECE schleuder2@example.org 2016-12-12\n\n\nThis key was deleted:\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") expect(mail.list.keys.size).to eql(list_keys.size - 2) end it 'deletes no key if the argument does not match' do mail = Mail.new mail.list = create(:list) mail.list.import_key(File.read('spec/fixtures/example_key.txt')) mail.body = 'ignore me' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: ['0x0x0x']).delete_key expect(output).to eql("Error: No key found with this fingerprint: '0x0x0x'.") expect(mail.list.keys.size).to eql(list_keys.size) end it 'sends error message if no argument is given' do mail = Mail.new mail.list = create(:list) mail.list.import_key(File.read('spec/fixtures/example_key.txt')) mail.body = 'ignore me' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).delete_key expect(output).to eql("Error: You did not send any arguments for the keyword 'DELETE-KEY'.\n\nOne is required, more are optional, e.g.:\nX-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3\n\nOr, to delete multiple keys at once:\nX-DELETE-KEY: 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3 a-subscription@hostname\n\nThe matching keys will be deleted only if the argument matches them distinctly.\n") expect(mail.list.keys.size).to eql(list_keys.size) end it 'returns a string as error message if input message has no content' do mail = Mail.new mail.list = create(:list) mail.body = '' mail.to_s list_keys = mail.list.keys output = KeywordHandlers::KeyManagement.new(mail: mail, arguments: []).add_key expect(output).to eql('Your message did not contain any attachments nor text content. Therefore no key could be imported.') expect(mail.list.keys.size).to eql(list_keys.size) end end end schleuder-5.0.1/spec/schleuder/unit/keyword_handlers/sign_this_spec.rb000066400000000000000000000057131502127241700262630ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::KeywordHandlers::SignThis do let(:keyword_method) { Schleuder::KeywordHandlersRunner::REGISTERED_KEYWORDS[:request]['sign-this'][:method] } before(:each) do end it 'responds to the configured keyword method' do instance = KeywordHandlers::SignThis.new(mail: Mail.new, arguments: []) expect(instance).to respond_to(keyword_method) end it 'signs body content if no attachments are present' do content = "something\nsomething\nsomething" mail = Mail.new mail.list = create(:list) ENV['GNUPGHOME'] = mail.list.listdir mail.body = content # Force mail to build its internal structure. mail.to_s instance = KeywordHandlers::SignThis.new(mail: mail, arguments: []) signed_text = instance.send(keyword_method) match_string = "BEGIN PGP SIGNED MESSAGE-----\nHash: SHA(256|512)\n\n#{content}\n-----BEGIN PGP SIGNATURE" # list.gpg.verify() results in a "Bad Signature". The sign-this keyword-handler # also uses GPGME::Crypto, apparently that makes a difference. crypto = GPGME::Crypto.new verification_string = '' crypto.verify(signed_text) do |sig| verification_string = sig.to_s end expect(signed_text).to match(match_string) expect(verification_string).to match('Good signature from D06350440F759BD3') end it 'signs attachment (even if a body is present)' do example_key = File.read('spec/fixtures/example_key.txt') expired_key = File.read('spec/fixtures/expired_key.txt') mail = Mail.new mail.list = create(:list) ENV['GNUPGHOME'] = mail.list.listdir mail.attachments['example_key.txt'] = { mime_type: 'application/pgp-key', content: example_key } mail.attachments['expired_key.txt'] = { mime_type: 'application/pgp-key', content: expired_key } mail.body = 'body is not relevant' # Force mail to build its internal structure. mail.to_s instance = KeywordHandlers::SignThis.new(mail: mail, arguments: ['arguments', 'are', 'not', 'relevant']) parts = instance.send(keyword_method) # list.gpg.verify() results in a "Bad Signature". The sign-this keyword-handler # also uses GPGME::Crypto, apparently that makes a difference. crypto = GPGME::Crypto.new verification_string1 = '' crypto.verify(parts[1].body.to_s, {signed_text: example_key}) do |sig| verification_string1 = sig.to_s end verification_string2 = '' crypto.verify(parts[2].body.to_s, {signed_text: expired_key}) do |sig| verification_string2 = sig.to_s end expect(parts.size).to be(3) expect(parts.map(&:to_s).join).not_to include('relevant') expect(parts.first).not_to be_blank expect(parts.first).not_to include('translation missing') expect(verification_string1).to match('Good signature from D06350440F759BD3') expect(verification_string2).to match('Good signature from D06350440F759BD3') end end schleuder-5.0.1/spec/schleuder/unit/keyword_handlers_runner_spec.rb000066400000000000000000000074221502127241700256640ustar00rootroot00000000000000require 'spec_helper' describe 'KeywordHandlersRunner' do it 'stores a keyword that was registered' do KeywordHandlersRunner.register_keyword( type: :request, keyword: 'a-keyword', handler_class: Object, handler_method: 'a_method', aliases: 'an-alias' ) expect(KeywordHandlersRunner::REGISTERED_KEYWORDS[:request]['a-keyword']).to be_a(Hash) expect(KeywordHandlersRunner::REGISTERED_KEYWORDS[:request]['an-alias']).to eql(KeywordHandlersRunner::REGISTERED_KEYWORDS[:request]['a-keyword']) end it 'requires X-LIST-NAME' do mail = Mail.new mail.body = 'x-list-keys' mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: create(:list), type: :request) expect(output).to eql(['Your message did not contain the required "X-LIST-NAME" keyword and was rejected.']) end it 'rejects X-LIST-NAME with mismatching argument' do mail = Mail.new mail.body = 'x-list-name: something' mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: create(:list), type: :request) expect(output).to eql(['Your message contained an incorrect "X-LIST-NAME" keyword. The keyword argument must match the email address of this list.']) end it 'rejects unknown keywords' do list = create(:list) mail = Mail.new mail.body = "x-list-subscriptions\nx-blabla\nx-list-name: #{list.email}" mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: list, type: :request) expect(output).to eql(["The given keyword 'blabla' is unknown. Please check its spelling or the documentation."]) end it 'does not require mandatory keywords if no keywords are present' do output = KeywordHandlersRunner.run(mail: Mail.new, list: create(:list), type: :request) expect(output).to eql([]) end it 'loads additional keyword handlers' do list = create(:list) mail = Mail.new mail.body = "x-custom-keyword\nx-list-name: #{list.email}\n" mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: list, type: :request) expect(CustomKeyword.ancestors).to include(Schleuder::KeywordHandlers::Base) expect(output).to eql(['Something something']) end it 'notifies admins' do list = create(:list, keywords_admin_notify: ['list-subscriptions']) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.new mail.list = list mail.body = "x-list-subscriptions\nx-list-name: #{list.email}\n" mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: list, type: :request) raw = Mail::TestMailer.deliveries.first message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup wanted_response = "Subscriptions:\n\nschleuder@example.org\t0x59C71FB38AEE22E091C78259D06350440F759BD3" expect(output).to eql([wanted_response]) expect(Mail::TestMailer.deliveries.count).to be(1) expect(message.to).to eql(['schleuder@example.org']) expect(message.subject).to eql('Notice') expect(message.first_plaintext_part.body.to_s).to include('list-subscriptions: ') expect(message.first_plaintext_part.body.to_s).to include(wanted_response) end it 'returns an error message if keyword is configured as admin-only' do list = create(:list, keywords_admin_only: ['list-subscriptions']) list.subscribe('subscription@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', false) mail = Mail.new mail.list = list mail.body = "x-list-subscriptions\nx-list-name: #{list.email}" mail.to_s output = KeywordHandlersRunner.run(mail: mail, list: list, type: :request) expect(output.length).to be(1) expect(output.first).to include("The keyword 'list-subscriptions' may only be used by list-admin.") expect(Mail::TestMailer.deliveries.count).to be(0) end end schleuder-5.0.1/spec/schleuder/unit/list_builder_spec.rb000066400000000000000000000077131502127241700234130ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::ListBuilder do it 'creates a new, valid list' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run expect(list).to be_an_instance_of Schleuder::List expect(list).to be_valid expect(messages).to be_blank end it 'returns an error-message if given an invalid email-address' do listname = "list-#{rand}" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run expect(list).to be_nil expect(messages).to be_an_instance_of Hash expect(messages.keys).to eq ['email'] expect(messages.values).to be_present end it 'returns an error-message if given an invalid email-address with a space' do listname = "list #{rand}@example.com" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run expect(list).to be_nil expect(messages).to be_an_instance_of Hash expect(messages.keys).to eq ['email'] expect(messages.values).to be_present end it 'creates a listdir for the list' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run expect(File.directory?(list.listdir)).to be true end it 'creates a list-key with all required UIDs' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run uids = list.key.uids.map(&:email) expect(uids.first).to eql(list.email) expect(uids).to include(list.request_address) expect(uids).to include(list.owner_address) end it 'subscribes the adminaddress and imports the adminkey' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run subscription_emails = list.subscriptions.map(&:email) keys_fingerprints = list.keys.map(&:fingerprint) expect(subscription_emails).to eq [adminaddress] expect(keys_fingerprints).to include('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end it 'subscribes the adminaddress and respects the given adminfingerprint' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, '59C71FB38AEE22E091C78259D06350440F759BD3').run subscription_emails = list.subscriptions.map(&:email) admin_subscription = list.admins.first expect(subscription_emails).to eq [adminaddress] expect(admin_subscription.fingerprint).to eql('59C71FB38AEE22E091C78259D06350440F759BD3') end it 'subscribes the adminaddress and ignores the adminfingerprint if an adminkey was given' do listname = "list-#{rand}@example.org" adminaddress = 'schleuder2@example.org' adminkey = File.read('spec/fixtures/example_key.txt') list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, '59C71FB38AEE22E091C78259D06350440F759BD3', adminkey).run subscription_emails = list.subscriptions.map(&:email) subscription_fingerprints = list.subscriptions.map(&:fingerprint) expect(subscription_emails).to eq [adminaddress] expect(subscription_fingerprints).to eq ['C4D60F8833789C7CAA44496FD3FFA6613AB10ECE'] end end schleuder-5.0.1/spec/schleuder/unit/list_spec.rb000066400000000000000000001265021502127241700217030ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::List do BOOLEAN_LIST_ATTRIBUTES = [ :send_encrypted_only, :receive_encrypted_only, :receive_signed_only, :receive_authenticated_only, :receive_from_subscribed_emailaddresses_only, :receive_admin_only, :keep_msgid, :bounces_drop_all, :bounces_notify_admins, :deliver_selfsent, :include_list_headers, :include_list_headers, :include_openpgp_header, :forward_all_incoming_to_admins, :set_reply_to_to_sender, :munge_from, ].freeze it 'has a valid factory' do list = create(:list_with_one_subscription) expect(list).to be_valid end it { is_expected.to respond_to :subscriptions } it { is_expected.to respond_to :email } it { is_expected.to respond_to :fingerprint } it { is_expected.to respond_to :log_level } it { is_expected.to respond_to :subject_prefix } it { is_expected.to respond_to :subject_prefix_in } it { is_expected.to respond_to :subject_prefix_out } it { is_expected.to respond_to :openpgp_header_preference } it { is_expected.to respond_to :internal_footer } it { is_expected.to respond_to :public_footer } it { is_expected.to respond_to :headers_to_meta } it { is_expected.to respond_to :bounces_drop_on_headers } it { is_expected.to respond_to :keywords_admin_only } it { is_expected.to respond_to :keywords_admin_notify } it { is_expected.to respond_to :send_encrypted_only } it { is_expected.to respond_to :receive_encrypted_only } it { is_expected.to respond_to :receive_signed_only } it { is_expected.to respond_to :receive_authenticated_only } it { is_expected.to respond_to :receive_from_subscribed_emailaddresses_only } it { is_expected.to respond_to :receive_admin_only } it { is_expected.to respond_to :keep_msgid } it { is_expected.to respond_to :bounces_drop_all } it { is_expected.to respond_to :bounces_notify_admins } it { is_expected.to respond_to :deliver_selfsent } it { is_expected.to respond_to :include_list_headers } it { is_expected.to respond_to :include_openpgp_header } it { is_expected.to respond_to :max_message_size_kb } it { is_expected.to respond_to :language } it { is_expected.to respond_to :forward_all_incoming_to_admins } it { is_expected.to respond_to :logfiles_to_keep } it { is_expected.to respond_to :set_reply_to_to_sender } it { is_expected.to respond_to :munge_from } it { is_expected.to respond_to :key_auto_import_from_email } it 'is invalid when email is nil' do # Don't use factory here because we'd run into List.listdir expecting email to not be nil. list = Schleuder::List.new(email: nil) expect(list).not_to be_valid expect(list.errors.messages[:email]).to include("can't be blank") end it 'is invalid when email is blank' do list = build(:list, email: '') expect(list).not_to be_valid expect(list.errors.messages[:email]).to include("can't be blank") end it 'is invalid when email does not contain an @' do list = build(:list, email: 'fooatbar.org') expect(list).not_to be_valid expect(list.errors.messages[:email]).to include('is not a valid email address') end it 'is invalid when email contains a space' do list = build(:list, email: 'foo bu@bar.org') expect(list).not_to be_valid expect(list.errors.messages[:email]).to include('is not a valid email address') end it 'is invalid when fingerprint is blank' do list = build(:list, fingerprint: '') expect(list).not_to be_valid expect(list.errors.messages[:fingerprint]).to include("can't be blank") end it 'is invalid when fingerprint is nil' do list = build(:list, fingerprint: nil) expect(list).not_to be_valid expect(list.errors.messages[:fingerprint]).to include("can't be blank") end it 'is invalid when fingerprint contains invalid characters' do list = build(:list, fingerprint: '&$$$$67923AAA') expect(list).not_to be_valid expect(list.errors.messages[:fingerprint]).to include('is not a valid OpenPGP-fingerprint') end BOOLEAN_LIST_ATTRIBUTES.each do |list_attribute| it "is invalid if #{list_attribute} is nil" do list = build(:list) list[list_attribute] = nil expect(list).not_to be_valid expect(list.errors.messages[list_attribute]).to include('must be true or false') end it "is invalid if #{list_attribute} is blank" do list = build(:list) list[list_attribute] = '' expect(list).not_to be_valid expect(list.errors.messages[list_attribute]).to include('must be true or false') end end [:headers_to_meta, :keywords_admin_only, :keywords_admin_notify].each do |list_attribute| it "is invalid if #{list_attribute} contains special characters" do list = build(:list) list[list_attribute] =['$from', 'to', 'date', 'cc'] expect(list).not_to be_valid expect(list.errors.messages[list_attribute]).to include('contains invalid characters') end it "is valid if #{list_attribute} does not contain special characters" do list = build(:list) list[list_attribute] = ['foobar'] expect(list).to be_valid end end it 'is invalid if bounces_drop_on_headers contains special characters' do list = build(:list, bounces_drop_on_headers: {'$' => '%'}) expect(list).not_to be_valid expect(list.errors.messages[:bounces_drop_on_headers]).to include('contains invalid characters') end [:subject_prefix, :subject_prefix_in, :subject_prefix_out].each do |list_attribute| it "is invalid if #{list_attribute} contains a linebreak" do list = build(:list) list[list_attribute] = "Foo\nbar" expect(list).not_to be_valid expect(list.errors.messages[list_attribute]).to include('must not include line-breaks') end it "is valid if #{list_attribute} is nil" do list = build(:list) list[list_attribute] = nil expect(list).to be_valid end end it 'is invalid if openpgp_header_preference is foobar' do list = build(:list, openpgp_header_preference: 'foobar') expect(list).not_to be_valid expect(list.errors.messages[:openpgp_header_preference]).to include('must be one of: sign, encrypt, signencrypt, unprotected, none') end [:max_message_size_kb, :logfiles_to_keep].each do |list_attribute| it "is invalid if #{list_attribute} is 0" do list = build(:list) list[list_attribute] = 0 expect(list).not_to be_valid expect(list.errors.messages[list_attribute]).to include('must be a number greater than zero') end end it 'is invalid if log_level is foobar' do list = build(:list, log_level: 'foobar') expect(list).not_to be_valid expect(list.errors.messages[:log_level]).to include('must be one of: debug, info, warn, error') end it 'is invalid if language is jp' do list = build(:list, language: 'jp') expect(list).not_to be_valid expect(list.errors.messages[:language]).to include('must be one of: en, de') end it 'is invalid if internal_footer includes a non-printable character' do list = build(:list, internal_footer: "\a") expect(list).not_to be_valid expect(list.errors.messages[:internal_footer]).to include('includes non-printable characters') end it 'is invalid if public_footer includes a non-printable character' do list = build(:list, public_footer: "\a") expect(list).not_to be_valid expect(list.errors.messages[:public_footer]).to include('includes non-printable characters') end describe '.configurable_attributes' do it 'returns an array that contains the configurable attributes' do expect(Schleuder::List.configurable_attributes).to eq [ :bounces_drop_all, :bounces_drop_on_headers, :bounces_notify_admins, :deliver_selfsent, :forward_all_incoming_to_admins, :headers_to_meta, :include_list_headers, :include_openpgp_header, :internal_footer, :keep_msgid, :key_auto_import_from_email, :keywords_admin_notify, :keywords_admin_only, :language, :log_level, :logfiles_to_keep, :max_message_size_kb, :munge_from, :openpgp_header_preference, :public_footer, :receive_admin_only, :receive_authenticated_only, :receive_encrypted_only, :receive_from_subscribed_emailaddresses_only, :receive_signed_only, :send_encrypted_only, :set_reply_to_to_sender, :subject_prefix, :subject_prefix_in, :subject_prefix_out, ] end it 'does not contain the attributes email and fingerprint' do expect(Schleuder::List.configurable_attributes).to_not include(:email) expect(Schleuder::List.configurable_attributes).to_not include(:fingerprint) end end describe '#fingerprint' do it 'transforms the fingerprint to upper case' do list = Schleuder::List.new(email: 'example@example.org', fingerprint: 'c4d60f8833789c7caa44496fd3ffa6613ab10ece') expect(list.fingerprint).to eq('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end it 'removes whitespaces and 0x from the fingerprint' do fingerprint = '0x 99 991 1000 10' list = build(:list, fingerprint: fingerprint) expect(list.fingerprint).to eq '99991100010' end end describe '#logfile' do it 'returns the logfile path' do list = create(:list, email: 'foo@bar.org') expect(list.logfile).to eq File.join(Schleuder::Conf.listlogs_dir, 'bar.org/foo/list.log') end end describe '#logger' do it 'calls the ListLogger' do list = create(:list) expect(Listlogger).to receive(:new).with(list) list.logger end end describe '#to_s' do it 'returns the email' do list = create(:list, email: 'foo@bar.org') expect(list.email).to eq 'foo@bar.org' end end describe '#admins' do it 'returns subscriptions of admin users' do list = create(:list) admin_subscription = create( :subscription, email: 'admin@foo.org', admin: true, list_id: list.id, ) _user_subscription = create( :subscription, email: 'user@foo.org', admin: false, list_id: list.id, ) expect(list.admins).to eq [admin_subscription] end end describe '#key' do it 'returns the key with the fingerprint of the list' do list = create( :list, fingerprint: '59C7 1FB3 8AEE 22E0 91C7 8259 D063 5044 0F75 9BD3' ) expect(list.key.fingerprint()).to eq '59C71FB38AEE22E091C78259D06350440F759BD3' end end describe '#secret_key' do it 'returns the secret key with the fingerprint of the list' do list = create( :list, fingerprint: '59C71FB38AEE22E091C78259D06350440F759BD3' ) expect(list.secret_key.secret?).to eq true expect(list.secret_key.fingerprint).to eq '59C71FB38AEE22E091C78259D06350440F759BD3' end end describe '#keys' do it 'it returns an array with the keys of the list' do list = create(:list) expect(list.keys).to be_kind_of Array expect(list.keys.length).to eq 1 end it 'returns an array of keys matching the given fingerprint' do list = create( :list, fingerprint: '59C71FB38AEE22E091C78259D06350440F759BD3' ) expect(list.keys).to be_kind_of Array expect(list.keys.first.fingerprint).to eq '59C71FB38AEE22E091C78259D06350440F759BD3' end it 'returns an array with the keys matching the given email address' do list = create(:list, email: 'schleuder@example.org') expect(list.keys('schleuder@example.org').length).to eq 1 expect( list.keys('schleuder@example.org').first.fingerprint ).to eq '59C71FB38AEE22E091C78259D06350440F759BD3' end it 'returns an array with the keys matching the given bracketed email address' do list = create(:list, email: 'schleuder@example.org') expect( list.keys('bla ').first.fingerprint ).to eq '59C71FB38AEE22E091C78259D06350440F759BD3' end end describe '#import_key' do it 'imports a given key' do list = create(:list) key = File.read('spec/fixtures/example_key.txt') expect { list.import_key(key) }.to change { list.keys.count }.by(1) list.delete_key('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end end describe '#delete_key' do it 'deletes the key with the given fingerprint' do list = create(:list) key = File.read('spec/fixtures/example_key.txt') list.import_key(key) expect do list.delete_key('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end.to change { list.keys.count }.by(-1) end it 'returns false if no key with the fingerprint was found' do list = create(:list) expect(list.delete_key('A4C60C8833789C7CAA44496FD3FFA6611AB10CEC')).to eq false end end describe '#export_key' do it 'exports the key with the fingerprint of the list if no argument is given' do list = create(:list, email: 'schleuder@example.org') expected_public_key = File.read('spec/fixtures/schleuder_at_example_public_key.txt') # Get rid of the first, opening line, so we don't compare against optional comments in the output, etc. expected_public_key = expected_public_key.split("\n").slice(1..-1).join("\n") expect(list.export_key()).to include expected_public_key end end describe '#key_minimal_base64_encoded' do it 'returns the key with the fingerprint of the list if no argument is given in an Autocrypt-compatible format' do list = create(:list) expect(list.key_minimal_base64_encoded()).to eq(File.read('spec/fixtures/schleuder_at_example_public_key_minimal_base64.txt')) end it 'does not return the key with the fingerprint in an Autocrypt-compatible format if the argument given does not correspond to a key' do list = create(:list) expect(list.key_minimal_base64_encoded('this fpr does not exist')).to be(false) end end it 'exports the key with the given fingerprint' do list = create(:list, email: 'schleuder@example.org') expected_public_key = File.read('spec/fixtures/schleuder_at_example_public_key.txt') # Get rid of the first, opening line, so we don't compare against optional comments in the output, etc. expected_public_key = expected_public_key.split("\n").slice(1..-1).join("\n") expect( list.export_key('59C71FB38AEE22E091C78259D06350440F759BD3') ).to include expected_public_key end describe '#check_keys' do it 'adds a message if a key expires in two weeks or less' do list = create(:list) key = double('key') generation_time = Time.now - 1.year expiry_time = Time.now + 7.days allow_any_instance_of(GPGME::Key).to receive(:subkeys).and_return(key) allow(key).to receive(:first).and_return(key) allow(key).to receive(:timestamp).and_return(generation_time) allow(key).to receive(:expires?).and_return(true) allow(key).to receive(:expired?).and_return(false) allow(key).to receive(:expired).and_return(false) allow(key).to receive(:any?).and_return(false) allow(key).to receive(:expires).and_return(expiry_time) allow(key).to receive(:fingerprint).and_return('59C71FB38AEE22E091C78259D06350440F759BD3') datefmt = '%Y-%m-%d' generation_date = generation_time.strftime(datefmt) expiry_date = expiry_time.strftime(datefmt) expect(list.check_keys).to eq "This key expires in 6 days:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org #{generation_date} [expires: #{expiry_date}]\n\n" end it 'adds a message if a key is revoked' do list = create(:list) allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:revoked) expect(list.check_keys).to eql("This key is revoked:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06 [revoked]\n\n") end it 'adds a message if a key is disabled' do list = create(:list) allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:disabled) expect(list.check_keys).to eql("This key is disabled:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06 [disabled]\n\n") end it 'adds a message if a key is invalid' do list = create(:list) allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:invalid) expect(list.check_keys).to eql("This key is invalid:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder@example.org 2016-12-06 [invalid]\n\n") end end describe '.by_recipient' do it 'returns the list for a given address' do list = create(:list, email: 'list@example.org') expect(Schleuder::List.by_recipient('list-owner@example.org')).to eq list end end describe '#sendkey_address' do it 'adds the sendkey keyword to the email address' do list = create(:list, email: 'list@example.org') expect(list.sendkey_address).to eq 'list-sendkey@example.org' end end describe '#request_address' do it 'adds the request keyword to the email address' do list = create(:list, email: 'list@example.org') expect(list.request_address).to eq 'list-request@example.org' end end describe '#owner_address' do it 'adds the owner keyword to the email address' do list = create(:list, email: 'list@example.org') expect(list.owner_address).to eq 'list-owner@example.org' end end describe '#bounce_address' do it 'adds the bounce keyword to the email address' do list = create(:list, email: 'list@example.org') expect(list.bounce_address).to eq 'list-bounce@example.org' end end describe '#gpg' do it 'returns an instance of GPGME::Ctx' do list = create(:list) expect(list.gpg).to be_an_instance_of GPGME::Ctx end it 'sets the GNUPGHOME environment variable to the listdir' do list = create(:list) list.gpg expect(ENV['GNUPGHOME']).to eq list.listdir end end context '#fetch_keys' do it 'fetches one key by fingerprint' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp) list = create(:list) list.subscribe('admin@example.org', nil, true) output = list.fetch_keys('98769E8A1091F36BD88403ECF71A3F8412D83889') expect(output).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'fetches one key by URL' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/keys\/example.asc/).and_return(resp) list = create(:list) list.subscribe('admin@example.org', nil, true) output = list.fetch_keys('http://somehost/keys/example.asc') expect(output).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'fetches one key by email address' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-email\/admin%40example.org/).and_return(resp) list = create(:list) list.subscribe('admin@example.org', nil, true) output = list.fetch_keys('admin@example.org') expect(output).to eql("This key was fetched (new key):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo 2010-08-13 [expired: 2017-01-20]\n") teardown_list_and_mailer(list) end it 'does not import non-self-signatures' do resp = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/openpgp-keys/public-key-with-third-party-signature.txt')) Typhoeus.stub(/by-fingerprint\/87E65ED2081AE3D16BE4F0A5EBDBE899251F2412/).and_return(resp) list = create(:list) list.delete_key('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') list.subscribe('admin@example.org', nil, true) output = list.fetch_keys('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') # GPGME apparently does not show signatures correctly in some cases, so we better use gpgcli. signature_output = list.gpg.class.gpgcli(['--list-sigs', '87E65ED2081AE3D16BE4F0A5EBDBE899251F2412'])[1].grep(/0F759BD3.*schleuder@example.org/) expect(output).to include("This key was fetched (new key):\n0x87E65ED2081AE3D16BE4F0A5EBDBE899251F2412 bla@foo") expect(signature_output).to be_empty teardown_list_and_mailer(list) end end describe 'send_list_key_to_subscriptions' do it 'sends its key to all subscriptions' do list = create(:list, send_encrypted_only: false) list.subscribe('admin@example.org', nil, true) list.send_list_key_to_subscriptions raw = Mail::TestMailer.deliveries.first expect(raw.parts.first.parts.first.body.to_s).to eql('Find the key for this address attached.') expect(raw.parts.first.parts.last.body.to_s).to include('4096R/59C71FB38AEE22E091C78259D06350440F759BD3') expect(raw.parts.first.parts.last.body.to_s).to include('-----BEGIN PGP PUBLIC KEY BLOCK-----') end end describe '#subscribe' do it 'subscribes and ignores nil-values for admin and delivery_enabled' do list = create(:list) sub, _ = list.subscribe('admin@example.org', nil, nil, nil) expect(sub.admin?).to be(false) expect(sub.delivery_enabled?).to be(true) end it 'subscribes and sets the fingerprint from key material that contains exactly one key' do list = create(:list) key_material = File.read('spec/fixtures/example_key.txt') sub, msgs = list.subscribe('admin@example.org', '', true, true, key_material) expect(msgs).to be(nil) expect(sub.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(list.subscriptions.size).to be(1) expect(list.subscriptions.first.fingerprint).to eql('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') expect(list.keys.size).to be(2) expect(list.keys.map(&:fingerprint)).to eql(['59C71FB38AEE22E091C78259D06350440F759BD3', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE']) end it 'subscribes and does not set the fingerprint from key material containing multiple keys' do list = create(:list) key_material = File.read('spec/fixtures/example_key.txt') key_material << File.read('spec/fixtures/olduid_key.txt') sub, msgs = list.subscribe('admin@example.org', '', true, true, key_material) expect(msgs).to eql('The given key material contained more than one key, could not determine which fingerprint to use. Please set it manually!') expect(sub.fingerprint).to be_blank expect(list.subscriptions.size).to be(1) expect(list.subscriptions.first.fingerprint).to be_blank expect(list.keys.size).to be(3) expect(list.keys.map(&:fingerprint)).to eql(['59C71FB38AEE22E091C78259D06350440F759BD3', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', '6EE51D78FD0B33DE65CCF69D2104E20E20889F66']) end it 'subscribes and does not set the fingerprint from key material containing no keys' do list = create(:list) key_material = 'blabla' sub, msgs = list.subscribe('admin@example.org', '', true, true, key_material) expect(msgs).to eql('The given key material did not contain any keys!') expect(sub.fingerprint).to be_blank expect(list.subscriptions.size).to be(1) expect(list.subscriptions.first.fingerprint).to be_blank expect(list.keys.size).to be(1) expect(list.keys.map(&:fingerprint)).to eql(['59C71FB38AEE22E091C78259D06350440F759BD3']) end it 'subscribes and ignores a given fingerprint if key material is given, too' do list = create(:list) key_material = 'blabla' sub, msgs = list.subscribe('admin@example.org', 'C4D60F8833789C7CAA44496FD3FFA6613AB10ECE', true, true, key_material) expect(msgs).to eql('The given key material did not contain any keys!') expect(sub.fingerprint).to be_blank expect(list.subscriptions.size).to be(1) expect(list.subscriptions.first.fingerprint).to be_blank expect(list.keys.size).to be(1) expect(list.keys.map(&:fingerprint)).to eql(['59C71FB38AEE22E091C78259D06350440F759BD3']) end end describe '#send_to_subscriptions' do it 'sends the message to all subscribers' do list = create(:list, send_encrypted_only: false) sub, msgs = list.subscribe('admin@example.org', nil, true) sub, msgs = list.subscribe('user1@example.org') sub, msgs = list.subscribe('user2@example.org') mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[0].subject).to eql('Something') expect(messages[1].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[1].subject).to eql('Something') expect(messages[2].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[2].subject).to eql('Something') teardown_list_and_mailer(list) end it "sends the message to all subscribers, in the clear if one's key is unusable, if send_encrypted_only is false" do list = create(:list, send_encrypted_only: false) sub, msgs = list.subscribe('admin@example.org', nil, true) key_material = File.read('spec/fixtures/expired_key.txt') sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org') mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[0].subject).to eql('Something') expect(messages[1].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[1].subject).to eql('Something') expect(messages[2].parts.first.parts.last.body.to_s).to eql('Some content') expect(messages[2].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message only to subscribers with available keys if send_encrypted_only is true, and a notification to the other subscribers' do list = create(:list, send_encrypted_only: true) sub, msgs = list.subscribe('admin@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) sub, msgs = list.subscribe('user1@example.org') sub, msgs = list.subscribe('user2@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3') mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[0].subject).to eql('Something') expect(messages[1].parts.first.body.to_s).to include('You missed an email') expect(messages[1].subject).to eql('Notice') expect(messages[2].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[2].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message only to subscribers with usable keys if send_encrypted_only is true, and a notification to the other subscribers' do list = create(:list, send_encrypted_only: true) key_material = File.read('spec/fixtures/partially_expired_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) key_material = File.read('spec/fixtures/expired_key.txt') sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3') mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].parts.first.body.to_s).to include('You missed an email') expect(messages[0].subject).to eql('Notice') expect(messages[1].parts.first.body.to_s).to include('You missed an email') expect(messages[1].subject).to eql('Notice') expect(messages[2].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[2].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message to all subscribers including the sender, if deliver_selfsent is true and the mail is correctly signed' do list = create(:list, send_encrypted_only: false, deliver_selfsent: true) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) key_material = File.read('spec/fixtures/example_key.txt') sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'user1@example.org' mail.subject = 'Something' mail.body = 'Some content' gpg_opts = { sign: true, sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3' } mail.gpg(gpg_opts) mail.deliver signed_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear Schleuder::Runner.new().run(signed_mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(list.deliver_selfsent).to be(true) expect(messages.size).to be(2) expect(recipients).to eql(['admin@example.org', 'user1@example.org']) expect(messages[0].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[0].subject).to eql('Something') expect(messages[1].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[1].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message to all subscribers but not the sender, if deliver_selfsent is false and the mail is correctly signed' do list = create(:list, send_encrypted_only: false, deliver_selfsent: false) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) key_material = File.read('spec/fixtures/example_key.txt') sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'admin@example.org' mail.subject = 'Something' mail.body = 'Some content' gpg_opts = { sign: true, sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3' } mail.gpg(gpg_opts) mail.deliver signed_mail = Mail::TestMailer.deliveries.first Mail::TestMailer.deliveries.clear Schleuder::Runner.new().run(signed_mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(list.deliver_selfsent).to be(false) expect(messages.size).to be(1) expect(recipients).to eql(['user1@example.org']) expect(messages[0].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[0].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message to all subscribers including the sender, if deliver_selfsent is false but the mail is not correctly signed' do list = create(:list, send_encrypted_only: false, deliver_selfsent: false) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) key_material = File.read('spec/fixtures/example_key.txt') sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'admin@example.org' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(list.deliver_selfsent).to be(false) expect(messages.size).to be(2) expect(recipients).to eql(['admin@example.org', 'user1@example.org']) expect(messages[0].parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') expect(messages[0].subject).to eql('Something') teardown_list_and_mailer(list) end it 'sends the message to subscribers if deliver_selfsent is set to false' do list = create(:list, send_encrypted_only: false, deliver_selfsent: false) sub, msgs = list.subscribe('admin@example.org', nil, true) sub, msgs = list.subscribe('user1@example.org', nil) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(2) expect(recipients).to eql(['admin@example.org', 'user1@example.org']) expect(messages.first.parts.first.parts.last.body.to_s).to eql('Some content') expect(messages.last.parts.first.parts.last.body.to_s).to eql('Some content') expect(messages.first.subject).to eql('Something') expect(messages.last.subject).to eql('Something') end end describe '#set_reply_to_to_sender' do it 'is disabled by default' do list = create(:list) expect(list.set_reply_to_to_sender).to be(false) teardown_list_and_mailer(list) end it 'does not set reply_to mail address when disabled' do list = create(:list, set_reply_to_to_sender: false) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].from).to eql([list.email]) expect(messages[0].reply_to).to be_nil expect(messages[1].from).to eql([list.email]) expect(messages[1].reply_to).to be_nil expect(messages[2].from).to eql([list.email]) expect(messages[2].reply_to).to be_nil teardown_list_and_mailer(list) end it 'sets reply-to to senders from-address when enabled' do list = create(:list, set_reply_to_to_sender: true) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].reply_to).to eql(mail.from) expect(messages[1].reply_to).to eql(mail.from) expect(messages[2].reply_to).to eql(mail.from) teardown_list_and_mailer(list) end it 'prefers reply_to of the sender over from when existing' do list = create(:list, set_reply_to_to_sender: true) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' mail.reply_to = 'abc@def.de' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].reply_to).to eql(mail.reply_to) expect(messages[1].reply_to).to eql(mail.reply_to) expect(messages[2].reply_to).to eql(mail.reply_to) teardown_list_and_mailer(list) end end describe '#munge_from' do it 'is disabled by default' do list = create(:list) expect(list.munge_from).to be(false) teardown_list_and_mailer(list) end it 'does not munge from address when disabled' do list = create(:list, munge_from: false) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0].from).to eql([list.email]) expect(messages[1].from).to eql([list.email]) expect(messages[2].from).to eql([list.email]) teardown_list_and_mailer(list) end it 'sets from to munged version when enabled' do list = create(:list, munge_from: true) key_material = File.read('spec/fixtures/default_list_key.txt') sub, msgs = list.subscribe('admin@example.org', nil, true, true, key_material) sub, msgs = list.subscribe('user1@example.org', nil, false, true, key_material) sub, msgs = list.subscribe('user2@example.org', nil, false, true, key_material) mail = Mail.new mail.to = list.email mail.from = 'something@localhost' mail.subject = 'Something' mail.body = 'Some content' Schleuder::Runner.new().run(mail.to_s, list.email) messages = Mail::TestMailer.deliveries recipients = messages.map { |m| m.to.first }.sort expect(messages.size).to be(3) expect(recipients).to eql(['admin@example.org', 'user1@example.org', 'user2@example.org']) expect(messages[0]['from'].to_s).to eql("\"#{mail.from.first} via #{list.email}\" <#{list.email}>") expect(messages[1]['from'].to_s).to eql("\"#{mail.from.first} via #{list.email}\" <#{list.email}>") expect(messages[2]['from'].to_s).to eql("\"#{mail.from.first} via #{list.email}\" <#{list.email}>") teardown_list_and_mailer(list) end end context '#refresh_keys' do it 'updates keys from the keyserver' do resp1 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp1) resp2 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/olduid_key_with_newuid.txt')) Typhoeus.stub(/by-fingerprint\/6EE51D78FD0B33DE65CCF69D2104E20E20889F66/).and_return(resp2) resp3 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/expired_key_extended.txt')) Typhoeus.stub(/by-fingerprint\/98769E8A1091F36BD88403ECF71A3F8412D83889/).and_return(resp3) list = create(:list) list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/expired_key.txt')) list.import_key(File.read('spec/fixtures/olduid_key.txt')) res = list.refresh_keys expect(res).to match(/This key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla@foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/) expect(res).to match(/This key was updated \(new user-IDs and new signatures\):\n0x6EE51D78FD0B33DE65CCF69D2104E20E20889F66 new@example.org \d{4}-\d{2}-\d{2}/) end it 'reports errors from refreshing keys' do resp = Typhoeus::Response.new(code: 503, body: 'Internal server error') Typhoeus.stub(/by-fingerprint/).and_return(resp) Typhoeus.stub(/search=/).and_return(resp) list = create(:list) list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/expired_key.txt')) res = list.refresh_keys expect(res).to match("Error while fetching data from the internet: Internal server error\nError while fetching data from the internet: Internal server error") end it 'does not import non-self-signatures' do resp1 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/openpgp-keys/public-key-with-third-party-signature.txt')) Typhoeus.stub(/by-fingerprint\/87E65ED2081AE3D16BE4F0A5EBDBE899251F2412/).and_return(resp1) resp2 = Typhoeus::Response.new(code: 200, body: File.read('spec/fixtures/default_list_key.txt')) Typhoeus.stub(/by-fingerprint\/59C71FB38AEE22E091C78259D06350440F759BD3/).and_return(resp2) list = create(:list) list.delete_key('87E65ED2081AE3D16BE4F0A5EBDBE899251F2412') list.subscribe('admin@example.org', nil, true) list.import_key(File.read('spec/fixtures/bla_foo_key.txt')) res = list.refresh_keys # GPGME apparently does not show signatures correctly in some cases, so we better use gpgcli. signature_output = list.gpg.class.gpgcli(['--list-sigs', '87E65ED2081AE3D16BE4F0A5EBDBE899251F2412'])[1].grep(/0F759BD3.*schleuder@example.org/) expect(res).to be_empty expect(signature_output).to be_empty end end end schleuder-5.0.1/spec/schleuder/unit/logger_notifications_spec.rb000066400000000000000000000106331502127241700251350ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::LoggerNotifications do context 'return path' do it 'sets default superadmin' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) list.logger.notify_admin('Something', nil, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.sender).to eql('root@localhost') expect(message[:Errors_To].to_s).to eql('root@localhost') end it 'sets superadmin' do oldval = Conf.instance.config['superadmin'] Conf.instance.config['superadmin'] = 'schleuder-admin@example.org' list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) list.logger.notify_admin('Something', nil, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.sender).to eql('schleuder-admin@example.org') expect(message[:Errors_To].to_s).to eql('schleuder-admin@example.org') Conf.instance.config['superadmin'] = oldval end end it 'notifies admins of simple text-message' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) list.logger.notify_admin('Something', nil, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['schleuder@example.org']) expect(message.subject).to eql(I18n.t('notice')) expect(message.first_plaintext_part.body.to_s).to eql('Something') end it 'notifies admins of multiple text-messages' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) list.logger.notify_admin(['Something', 'anotherthing'], nil, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['schleuder@example.org']) expect(message.subject).to eql(I18n.t('notice')) expect(message.parts.first.parts.first.body.to_s).to eql('Something') expect(message.parts.first.parts.last.body.to_s).to eql('anotherthing') end it 'notifies admins of multiple text-messages and the original message' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) mail = Mail.new mail.subject = 'A subject' list.logger.notify_admin(['Something', 'anotherthing'], mail.to_s, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.to).to eql(['schleuder@example.org']) expect(message.subject).to eql(I18n.t('notice')) expect(message.parts.first.parts.first.body.to_s).to eql('Something') expect(message.parts.first.parts[1].body.to_s).to eql('anotherthing') expect(message.parts.first.parts[2].body.to_s).to include('Subject: A subject') expect(message.parts.first.parts[2][:content_type].content_type).to eql('message/rfc822') end it 'notifies admins encryptedly if their key is usable' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', '59C71FB38AEE22E091C78259D06350440F759BD3', true) mail = Mail.new mail.subject = 'A subject' list.logger.notify_admin(['Something', 'anotherthing'], mail.to_s, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.subject).to eql('Notice') expect(message.parts.size).to be(2) expect(message.parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----') end it 'notifies admins in the clear if their key is unusable' do list = create(:list, send_encrypted_only: false) key_material = File.read('spec/fixtures/partially_expired_key.txt') list.subscribe('schleuder@example.org', nil, true, true, key_material) mail = Mail.new mail.subject = 'A subject' list.logger.notify_admin('Something', mail.to_s, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.subject).to eql('Notice') expect(message.parts.size).to be(2) expect(message.parts.first.parts.first.body.to_s).to eql('Something') end it 'includes a List-Id header in notification mails sent to admins' do list = create(:list, send_encrypted_only: false) list.subscribe('schleuder@example.org', nil, true) mail = Mail.new list.logger.notify_admin('Something', mail.to_s, I18n.t('notice')) message = Mail::TestMailer.deliveries.first expect(message.header['List-Id'].to_s).to eql("<#{list.email.gsub('@', '.')}>") end end schleuder-5.0.1/spec/schleuder/unit/message_spec.rb000066400000000000000000000332231502127241700223510ustar00rootroot00000000000000require 'spec_helper' describe Mail::Message do it "doesn't change the order of mime-parts" do text_part = Mail::Part.new text_part.body = 'This is text' image_part = Mail::Part.new image_part.content_type = 'image/png' image_part.content_disposition = 'attachment; filename=spec.png' message = Mail.new message.parts << image_part message.parts << text_part # This triggers the sorting. message.to_s expect(message.parts.first.mime_type).to eql('image/png') expect(message.parts.last.mime_type).to eql('text/plain') end # TODO: test message with "null" address ("<>") as Return-Path. I couldn't # bring Mail to generate such a message, yet. it 'recognizes a message sent to listname-bounce@hostname as automated message' do list = create(:list) mail = Mail.new # Trigger the setting of mandatory headers. mail.to_s mail = Mail.create_message_to_list(mail.to_s, 'something-bounce@localhost', list).setup expect(mail.automated_message?).to be(true) end it 'recognizes bounce message subject using the bounce_email gem' do list = create(:list) mail = Mail.new mail.subject = 'Undelivered Mail Returned to Sender' mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(true) end Dir.glob('spec/fixtures/mails/not_bounces/*') do |filename| it "does not misclassify normal message #{filename} as bounce" do list = create(:list) mail = Mail.new(File.read(filename)) mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(false) end end Dir.glob('spec/fixtures/mails/bounces/*') do |filename| it "does not misclassify bounce #{filename} as normal message" do list = create(:list) mail = Mail.new(File.read(filename)) mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(true) end end it "recognizes a cron message with 'Auto-Submitted'-header NOT as automated message" do list = create(:list) mail = Mail.new mail.header['Auto-Submitted'] = 'yes' mail.header['X-Cron-Env'] = '' # Trigger the setting of mandatory headers. mail.to_s mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(false) end it "recognizes a Jenkins message with 'Auto-Submitted'-header NOT as automated message" do list = create(:list) mail = Mail.new mail.header['Auto-submitted'] = 'auto-generated' mail.header['X-Jenkins-Job'] = 'test_Tails_ISO_stable' # Trigger the setting of mandatory headers. mail.to_s mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(false) end # https://0xacab.org/schleuder/schleuder/issues/248 it "recognizes a sudo message with 'Auto-Submitted'-header NOT as automated message" do list = create(:list) mail = Mail.new mail.header['Auto-submitted'] = 'auto-generated' mail.subject = '*** SECURITY information for host.example.com ***' # Trigger the setting of mandatory headers. mail.to_s mail = Mail.create_message_to_list(mail.to_s, 'something@localhost', list).setup expect(mail.automated_message?).to be(false) end context '#add_subject_prefix!' do it 'adds a configured subject prefix' do list = create(:list) list.subject_prefix = '[prefix]' list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.from 'someone@example.org' mail.to list.email mail.text_part = 'blabla' mail.subject = 'test' message = Mail.create_message_to_list(mail.to_s, list.email, list).setup message.add_subject_prefix! expect(message.subject).to eql('[prefix] test') end it 'adds a configured subject prefix without subject' do list = create(:list) list.subject_prefix = '[prefix]' list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.from 'someone@example.org' mail.to list.email mail.text_part = 'blabla' message = Mail.create_message_to_list(mail.to_s, list.email, list).setup message.add_subject_prefix! expect(message.subject).to eql('[prefix]') end it 'does not add a subject prefix if already present' do list = create(:list) list.subject_prefix = '[prefix]' list.subscribe('admin@example.org', nil, true) mail = Mail.new mail.from 'someone@example.org' mail.to list.email mail.text_part = 'blabla' mail.subject = 'Re: [prefix] test' message = Mail.create_message_to_list(mail.to_s, list.email, list).setup message.add_subject_prefix! expect(message.subject).to eql('Re: [prefix] test') end end it 'adds list#public_footer as last mime-part without changing its value' do footer = "\n\n-- \nblabla\n blabla\n " list = create(:list) list.public_footer = footer mail = Mail.new mail.body = 'blabla' mail.list = list mail.add_public_footer! expect(mail.parts.last.body.to_s).to eql(footer) end it 'adds list#internal_footer as last mime-part without changing its value' do footer = "\n\n-- \nblabla\n blabla\n " list = create(:list) list.internal_footer = footer mail = Mail.new mail.body = 'blabla' mail.list = list mail.add_internal_footer! expect(mail.parts.last.body.to_s).to eql(footer) end context 'makes a pseudo header' do it 'with key / value' do mail = Mail.new ph = mail.make_pseudoheader('notice', 'some value') expect(ph).to eql('Notice: some value') end it 'without value' do mail = Mail.new ph = mail.make_pseudoheader(:key, nil) expect(ph).to eql('Key: ') end it 'with empty value' do mail = Mail.new ph = mail.make_pseudoheader(:key, '') expect(ph).to eql('Key: ') end it 'that is getting wrapped' do mail = Mail.new ph = mail.make_pseudoheader('notice', 'adds list#public_footer as last mime-part without changing its value adds list#public_footer as last mime-part without changing its value') expect(ph).to eql("Notice: adds list#public_footer as last mime-part without changing its value\n adds list#public_footer as last mime-part without changing its value") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end it 'that multiline are getting wrapped' do mail = Mail.new ph = mail.make_pseudoheader('notice', "adds list#public_footer as last mime-part\nwithout changing its value adds list#public_footer as last mime-part without changing its value") expect(ph).to eql("Notice: adds list#public_footer as last mime-part\n without changing its value adds list#public_footer as last mime-part without\n changing its value") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end it 'that single multiline are getting indented' do mail = Mail.new ph = mail.make_pseudoheader('notice', "on line 1\non line 2 but indented") expect(ph).to eql("Notice: on line 1\n on line 2 but indented") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end it 'that a line with less than 76 gets wrapped' do mail = Mail.new ph = mail.make_pseudoheader('keylongerthan8', 'afafa afafaf' * 6) # message is 72 long expect(ph).to eql("Keylongerthan8: afafa afafafafafa afafafafafa afafafafafa afafafafafa\n afafafafafa afafaf") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end it 'that a multiline with less than 76 get wrapped correctly on the first line' do mail = Mail.new ph = mail.make_pseudoheader('keylongerthan8', ('afafa afafaf' * 6)+"\nbla bla newline") expect(ph).to eql("Keylongerthan8: afafa afafafafafa afafafafafa afafafafafa afafafafafa\n afafafafafa afafaf\n bla bla newline") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end it 'that a multiline with less than 76 get wrapped correctly on the first line and the following lines' do mail = Mail.new ph = mail.make_pseudoheader('keylongerthan8', ('afafa afafaf' * 6)+"\nbla bla newline"+('afafa afafaf' * 6)) expect(ph).to eql("Keylongerthan8: afafa afafafafafa afafafafafa afafafafafa afafafafafa\n afafafafafa afafaf\n bla bla newlineafafa afafafafafa afafafafafa afafafafafa afafafafafa\n afafafafafa afafaf") expect(ph.split("\n")).to all( satisfy{|l| l.length <= 78 }) end end context '.keywords' do it 'stops looking for keywords when a blank line that is not followed by another keyword is met' do string = <<~EOS x-something: bla x-somethingelse: ok something x-toolate: tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['bla']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("something\nx-toolate: tralafiti\n") end it 'reads multiple lines as keyword arguments' do string = <<~EOS x-something: first second third x-somethingelse: ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'second', 'third']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("tralafiti\n") end it 'takes the whole rest of the body as keyword argument if blank lines are present' do string = <<~EOS x-something: first second third ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'second', 'third', 'ok', 'tralafiti']]]) expect(m.body.to_s).to eql('') end it 'drops empty lines in keyword arguments parsing' do string = <<~EOS x-something: first third x-somethingelse: ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'third']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("tralafiti\n") end it 'drops multiple empty lines between keywords and content' do string = <<~EOS x-something: first third x-somethingelse: ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'third']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("tralafiti\n") end it 'splits lines into words and downcases them in keyword arguments' do string = <<~EOS x-something: first SECOND end third x-somethingelse: ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'second', 'end', 'third']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("tralafiti\n") end it 'ignores empty lines before keywords' do string = <<~EOS x-something: first third x-somethingelse: ok tralafiti EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([['something', ['first', 'third']], ['somethingelse', ['ok']]]) expect(m.body.to_s).to eql("tralafiti\n") end it 'stops looking for keywords when the first line is already email content' do string = <<~EOS Please ignore this message, i am trying to debug a possible schleuder bug. Here is a schleuder keyword command in the middle of the message text: X-LIST-NAME: foo@example.org X-ATTACH-LISTKEY: -----BEGIN PGP PUBLIC KEY BLOCK----- nothing to see here. And here is some followup text. EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([]) expect(m.body.to_s).to include("X-LIST-NAME: foo@example.org\nX-ATTACH-LISTKEY:\n-----BEGIN PGP PUBLIC KEY BLOCK-----") end it 'stops looking for keywords when already the first line is blank followed by email content' do string = <<~EOS Please ignore this message, i am trying to debug a possible schleuder bug. Here is a schleuder keyword command in the middle of the message text: X-LIST-NAME: foo@example.org X-ATTACH-LISTKEY: -----BEGIN PGP PUBLIC KEY BLOCK----- nothing to see here. And here is some followup text. EOS m = Mail.new m.body = string m.to_s keywords = m.keywords expect(keywords).to eql([]) expect(m.body.to_s).to include("X-LIST-NAME: foo@example.org\nX-ATTACH-LISTKEY:\n-----BEGIN PGP PUBLIC KEY BLOCK-----") end end it 'verifies an encapsulated (signed-then-encrypted) message' do list = create(:list, fingerprint: '7EDF3336CB8BC6D15D461DB5FFF7A04251E7D112') list.import_key(File.read('spec/fixtures/openpgp-keys/encapsulated-list.sec')) list.import_key(File.read('spec/fixtures/openpgp-keys/encapsulated-sender.pub')) list.subscribe('admin@example.org', '9F29CA4CF1A47561492C0737C7A5457A35C50082', true) msg = File.read('spec/fixtures/mails/encapsulated.eml') mail = Mail.create_message_to_list(msg, list.request_address, list) result = mail.setup expect(result.was_validly_signed?).to be(true) end end schleuder-5.0.1/spec/schleuder/unit/subscription_spec.rb000066400000000000000000000113021502127241700234430ustar00rootroot00000000000000require 'spec_helper' describe Schleuder::Subscription do BOOLEAN_SUBSCRIPTION_ATTRIBUTES = [ :delivery_enabled, :admin ].freeze it 'has a valid factory' do subscription = create(:subscription) expect(subscription).to be_valid end it { is_expected.to respond_to :list_id } it { is_expected.to respond_to :email } it { is_expected.to respond_to :fingerprint } it { is_expected.to respond_to :admin } it { is_expected.to respond_to :delivery_enabled } it 'is invalid when list_id is blank' do subscription = build(:subscription, list_id: '') expect(subscription).not_to be_valid expect(subscription.errors.messages[:list_id]).to be_present end it 'is invalid when email is nil' do list = create(:list) subscription = build(:subscription, list_id: list.id, email: nil) expect(subscription).not_to be_valid expect(subscription.errors.messages[:email]).to include("can't be blank") end it 'is invalid when email is blank' do list = create(:list) subscription = build(:subscription, list_id: list.id, email: '') expect(subscription).not_to be_valid expect(subscription.errors.messages[:email]).to include("can't be blank") end it 'is invalid when email does not contain an @' do list = create(:list) subscription = build(:subscription, list_id: list.id, email: 'fooatbar.org') expect(subscription).not_to be_valid expect(subscription.errors.messages[:email]).to include('is not a valid email address') end it 'formats email address when email begins with a space' do list = create(:list) subscription = build(:subscription, list_id: list.id, email: ' foo@bar.org') expect(subscription).to be_valid expect(subscription.email).to be_eql('foo@bar.org') expect(subscription.errors.messages[:email]).to be_blank end it 'is valid when fingerprint is empty' do list = create(:list) subscription = build(:subscription, list_id: list.id, fingerprint: '') expect(subscription).to be_valid expect(subscription.errors.messages[:fingerprint]).to be_blank end it 'is valid when fingerprint is nil' do list = create(:list) subscription = build(:subscription, list_id: list.id, fingerprint: nil) expect(subscription).to be_valid expect(subscription.errors.messages[:fingerprint]).to be_blank end it 'is invalid when fingerprint contains invalid characters' do list = create(:list) subscription = build(:subscription, list_id: list.id, fingerprint: '&$$$$123AAA') expect(subscription).not_to be_valid expect(subscription.errors.messages[:fingerprint]).to include('is not a valid OpenPGP-fingerprint') end BOOLEAN_SUBSCRIPTION_ATTRIBUTES.each do |subscription_attribute| it "is invalid if #{subscription_attribute} is nil" do list = create(:list) subscription = build(:subscription, list_id: list.id) subscription[subscription_attribute] = nil expect(subscription).not_to be_valid expect(subscription.errors.messages[subscription_attribute]).to include('must be true or false') end it "is invalid if #{subscription_attribute} is blank" do list = create(:list) subscription = build(:subscription, list_id: list.id) subscription[subscription_attribute] = '' expect(subscription).not_to be_valid expect(subscription.errors.messages[subscription_attribute]).to include('must be true or false') end end it 'is invalid if the given email is already subscribed for the list' do list1 = create(:list) list2 = create(:list) subscription1 = create(:subscription, list_id: list1.id) subscription2 = create(:subscription, list_id: list2.id, email: subscription1.email) subscription3 = build(:subscription, email: subscription1.email, list_id: subscription1.list_id) expect(subscription1).to be_valid expect(subscription2).to be_valid expect(subscription3).not_to be_valid expect(subscription3.errors[:email]).to eql(['is already subscribed']) end it 'downcases all letters of an email address' do subscription = create(:subscription, email: 'AbcDEF@example.net') expect(subscription.email).to eq('abcdef@example.net') end describe '#fingerprint' do it 'transforms the fingerprint to upper case' do subscription = Schleuder::Subscription.new(email: 'example@example.org', fingerprint: 'c4d60f8833789c7caa44496fd3ffa6613ab10ece') expect(subscription.fingerprint).to eq('C4D60F8833789C7CAA44496FD3FFA6613AB10ECE') end end it 'removes whitespaces and 0x from the fingerprint' do fingerprint = '0x 99 991 1000 10' subscription = build(:subscription, fingerprint: fingerprint) expect(subscription.fingerprint).to eq '99991100010' end end schleuder-5.0.1/spec/smtp-daemon.rb000077500000000000000000000032311502127241700172010ustar00rootroot00000000000000#!/usr/bin/env ruby # # This script is a very simple SMTP-daemon, that dumps every incoming email # into the given directory. It's meant to capture messages from schleuder-lists # during test-runs. require 'socket' require 'open3' trap ('INT') { exit 0 } def usage puts "Usage: #{File.basename(__FILE__)} portnum output-directory" exit 1 end # get args if ARGV.first.to_s.match('(-h|--help|help)') || ARGV.empty? usage end port = ARGV.first.to_i if port == 0 usage end outputdir = ARGV[1].to_s if outputdir.empty? usage elsif ! File.directory?(outputdir) puts "Not a directory: #{outputdir}" exit 1 end begin # run the server server = TCPServer.new('localhost', port) # receive input while (connection = server.accept) input = '' recipient = '' connection.puts '220 localhost SMTP' begin while line = connection.gets line.chomp! case line[0..3].downcase when 'ehlo', 'helo' connection.puts '250 localhost' when 'mail', 'rset' connection.puts '250 ok' when 'rcpt' recipient = line.split(':').last.gsub(/[<>\s]*/, '') connection.puts '250 ok' when 'data' connection.puts '354 go ahead' when 'quit' connection.puts '221 localhost' when '.' filename = File.join(outputdir, "mail-#{Time.now.to_f}") # puts "New message to #{recipient} written to #{filename}" IO.write(filename, input) connection.puts '250 ok' else input << line + "\n" end end rescue IOError end connection.close end rescue => exc $stderr.puts exc exit 1 end schleuder-5.0.1/spec/spec_helper.rb000066400000000000000000000112761502127241700172530ustar00rootroot00000000000000ENV['SCHLEUDER_ENV'] ||= 'test' ENV['SCHLEUDER_CONFIG'] = 'spec/schleuder.yml' ENV['SCHLEUDER_LIST_DEFAULTS'] = 'etc/list-defaults.yml' if ENV['USE_BUNDLER'] != 'false' require 'bundler/setup' Bundler.setup end # We need to do this before requiring any other code # Check env if we want to run code coverage analysis if ENV['CHECK_CODE_COVERAGE'] == 'true' require 'simplecov' require 'simplecov-html' SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter SimpleCov.start do add_filter %r{^/vendor/} add_filter %r{^/spec/} end end require 'schleuder' require 'schleuder/cli' require 'database_cleaner' require 'factory_bot' require 'net/http' require 'fileutils' require 'securerandom' if ENV['USE_BYEBUG'] != 'false' require 'byebug' end # Forcing a specific timezone and locale because some tests match on dates and strings. ENV['TZ'] = 'Etc/UTC' ENV['LANG'] = 'en_US.UTF-8' RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end config.order = :random config.include FactoryBot::Syntax::Methods config.before(:suite) do FactoryBot.find_definitions end config.before(:suite) do DatabaseCleaner.strategy = :deletion DatabaseCleaner.clean_with(:truncation) end config.before(:each) do Typhoeus::Expectation.clear end config.around(:each) do |example| Mail::TestMailer.deliveries.clear DatabaseCleaner.cleaning do example.run end end config.after(:each) do |example| FileUtils.rm_rf(Dir['spec/gnupg/pubring.gpg~']) end config.after(:suite) do cleanup_gnupg_home stop_smtp_daemon end # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end Mail.defaults do delivery_method :test end # Block all unstubbed connections Typhoeus::Config.block_connection = true def cleanup_gnupg_home ENV['GNUPGHOME'] = nil FileUtils.rm_rf Schleuder::Conf.lists_dir end def smtp_daemon_outputdir File.join(Conf.lists_dir, 'smtp-daemon-output') end def start_smtp_daemon if File.directory?(smtp_daemon_outputdir) # Try to kill it, in case it's still around (this occurred on some # systems). stop_smtp_daemon end if ! File.directory?(smtp_daemon_outputdir) FileUtils.mkdir_p(smtp_daemon_outputdir) end daemon = File.join('spec', 'smtp-daemon.rb') pid = Process.spawn(daemon, '2523', smtp_daemon_outputdir) pidfile = File.join(smtp_daemon_outputdir, 'pid') IO.write(pidfile, pid) end def stop_smtp_daemon pidfile = File.join(smtp_daemon_outputdir, 'pid') if File.exist?(pidfile) pid = File.read(pidfile).to_i Process.kill(15, pid) FileUtils.rm_rf smtp_daemon_outputdir end end def run_schleuder(command, email, message_path) `SCHLEUDER_ENV=test SCHLEUDER_CONFIG=spec/schleuder.yml bin/schleuder #{command} #{email} < #{message_path} 2>&1` end def run_cli(command) `SCHLEUDER_ENV=test SCHLEUDER_CONFIG=spec/schleuder.yml bin/schleuder #{command} 2>&1` end def with_env(env) backup = ENV.to_hash ENV.replace(env) yield ensure ENV.replace(backup) end def process_mail(msg, recipient) output = nil begin output = Schleuder::Runner.new.run(msg, recipient) rescue SystemExit end output end def teardown_list_and_mailer(list) FileUtils.rm_rf(list.listdir) Mail::TestMailer.deliveries.clear end def encrypt_string(list, str) _, ciphertext, _ = list.gpg.class.gpgcli("--recipient #{list.fingerprint} --encrypt") do |stdin, stdout, stderr| stdin.puts str # Apparently it differs between ruby-version if we have to close the stream manually. stdin.close if ! stdin.closed? stdout.readlines end ciphertext.reject { |line| line.match(/^\[GNUPG:\]/) }.join end def capture_output orig_stdout = $stdout $stdout = StringIO.new orig_stderr = $stderr $stderr = StringIO.new exitcode = nil begin yield rescue SystemExit => exc exitcode = exc.status end $stdout.rewind output = $stdout.read $stderr.rewind errors = $stderr.read # Cleanup $stderr = orig_stderr $stdout = orig_stdout [output, errors, exitcode] end def with_tmpfile(content, &blk) file = File.new(File.join(Conf.lists_dir, SecureRandom.hex(32)), 'w+') begin file.write(content) file.close yield file.path ensure File.unlink(file) end end def t(key, **kwargs) I18n.t(key, **kwargs) end end