요약

  • RFC 3028은 메일 필터링을 제약된 동작 언어로 만들고, 스크립트가 배달에 영향을 주는 동작을 하나도 선택하지 않았을 때 메시지를 보호하도록 암묵적 keep를 사용했다.
  • keep,fileinto,redirect,discard및 거절은 인터프리터 경계에서의 처리 방향을 기술했다. 저장, 이후 수신 거부, 최종 관찰은 여전히 실행 중인 메일 시스템의 몫이었다.

2001년 1월, 서버 측 필터링은 애매한 설계 문제를 안고 있었다. 사용자는 메일박스를 열기 전에 메일이 분류되고, 전달되고, 거부되기를 원했다. 운영자는 배달 서버에서 모든 사용자에게 범용 프로그램을 안전하게 줄 수는 없었다. 임의 코드를 실행할 만큼 표현력 있는 언어는 편의를 거의 통제 불가능한 보안 경계로 바꿔 놓을 것이다.

RFC 3028의 답은 힘을 제거하는 것이었다. Sieve에는 루프도, 함수도, 변수도 없고 외부 셸로 향하는 경로도 없었다. 검사는 부작용 없이 메시지의 사실을 살펴보았다. 제어 명령은 어떤 블록이 실행될지 고르고, 동작 명령은 메시지에 무엇을 해야 하는지 요청했다. 이것은 단지 문법적 정리가 아니었다. 관찰과 변형을 분리하고, 변형 어휘를 추론 가능할 만큼 작게 유지한 것이다.

이 설계에서 가장 계몽적인 부분은 아무 동작도 실행되지 않았을 때 무슨 일이 일어나는가였다.

빈 분기는 안전한 의미를 갖게 되었다

필터링 규칙은 경우를 놓친다. 발신자가 주소를 바꾸고, 메일링 리스트가 헤더를 바꾸고, 조건이 단순히 잘못 쓰였을 수 있다. 파괴적인 시스템에서는 모든 분기를 통과해 떨어지는 것이 우연히 메일을 폐기할 수 있었다. 그래서 RFC 3028은 암묵적 keep를 정의했다: 동작이 이를 취소하지 않는 한, 시스템은 필터링 없이 사용했을 것과 동일한 기본 처리를 수행한다. 일반적으로 메시지를 사용자의 기본 메일박스에 넣는 것이다.

암묵적 keep는 모든 결과에 무음으로 덧붙여지는 두 번째 복사본이 아니었다.keep,fileinto,redirect,discard는 이를 취소했다. 인터프리터는 각 분기 후에 무조건적인 안전 복사본을 실행하는 것이 아니라 동작 집합을 평가하고 있었다. 일부 후속 확장은 기본값을 취소하지 않은 채 부작용을 만들 수 있었지만, 그렇게 하려면 명시해야 했다. "이 동작이 암묵적 keep를 취소하는가?"라는 질문은 그 동작의 계약의 일부가 되었다.

그 계약은 겉보기에 이상한 규칙도 설명한다.discard는 다른 모든 동작을 파괴하지 않았다. 암묵적 keep를 조용히 취소했을 뿐이다. 스크립트가fileinto를 수행한 다음discard를 수행하면, 파일링된 복사본은 여전히 일어났다. discard는 추가적인 기본 복사본만 막은 것이다. 그 동사는 절대적으로 들렸지만 정의된 효과는 관계적이었다: 호환되는 동작은 그대로 두고 폴백만 제거한다.

처리 동사는 제어 경계에서 멈췄다

이 언어는 구체적인 동사를 사용했지만, 각 동사는 스크립트 밖의 기계 장치에 의존했다.

keep은 구현의 보통 기본 처리를 요청했다. 그 덕분에 스크립트 작성자는 기본 메일박스가INBOX라고 불리는지, 파일로 표현되는지, 다른 저장소에서 제공되는지 알 필요가 없었다. 이 추상화가 유용했던 것은 바로 명령이 저장 메커니즘을 기술하지 않았기 때문이다. 성공적인 분기 선택은 디스크 공간이 존재하고, 권한이 유효하며, 메일박스 커밋이 완료되었음을 증명할 수 없었다.

fileinto는 폴더 이름을 지정했다. RFC 3028은 일부 환경에서는 제공할 수 없다는 점을 인정하면서 지원을 권고했다. 따라서 폴더 문자열은 폴더가 존재하거나 쓰기가 영속화되었다는 증거가 아니라 배달 에이전트에 대한 지시였다.

redirect는 더욱 계몽적이었다. 봉투 수신자를 바꾸고 MTA 포워딩 방식으로 메시지를 바깥으로 보냈다. 로컬 인터프리터는 그 경로를 선택하고 시도할 수 있었다. 원격 서버는 여전히 수신 여부를 통제했고, 이후 시스템은 그곳에서 "배달됨"이 무엇을 의미하는지 통제했다. 루프 감지 역시 구현의 의무였다.redirect라는 단어는 새로운 배달 사건을 시작했지, 그것을 닫지 않았다.

discard는 의도적으로 실패 통지를 만들지 않았다. 무음은 한 종류의 피드백을 줄였지만, 발신자가 볼 수 있는 접수증도 제거했다. 스크립트 결정의 증거는 운영자가 보존했다면 실행 시스템의 기록에서 와야 했다. 반송이 없다는 것은 저장, 포워딩, 심지어 특정 필터링 이유의 증거가 아니었다.

동작 상호작용은 부작용에 대한 거버넌스였다

여러 동작이 하나의 메시지에 적용될 수 있게 되자, 그 조합이 개별 이름보다 더 중요해졌다. RFC 3028은 확장이 기본 동작과의 상호작용을 정의할 것을 요구했다. 사이트는 몇 개의 동작이 실행될 수 있는지, 어떤 조합이 허용되는지를 제한할 수 있었다. 구현은 스크립트가 같은 메일박스에 두 번 요청하더라도 중복 배달을 피해야 했다.

이 규칙들은 증폭과 모순을 통제했다. 반복적으로 redirect할 수 있는 사용자는 메일폭탄을 만들 수 있었다. 배달과 거절을 동시에 하는 스크립트는 복사본을 유지하면서 발신자에게 메시지가 실패했다고 말할 수 있었다. 모든 동작을 독립적으로 다루는 시스템은 단일 규칙 작성자가 의도하지 않은 결과를 만들어 냈을 것이다.

이후의 표준들은 이 점을 더욱 날카롭게 했다. RFC 5228은 RFC 3028을 대체하면서 암묵적 keep와 기본 처리 모델을 유지했고, 오류 처리와 확장 상호작용을 더 명확하게 만들었다. RFC 9122는 결국 IANA Sieve Actions 레지스트리를 만들었고, 그 열에는 동작 상호작용과 동작이 암묵적 keep를 취소하는지 여부가 포함된다. 레지스트리는 구조적 질문을 명시적 메타데이터로 바꾸었다. 그것은 계약을 목록화했을 뿐, 어떤 특정 동작이 성공했는지는 보고하지 않았다.

거절은 결정과 전송을 혼동하는 비용을 드러냈다

원래의 reject 확장은 경계가 배달 순서의 잘못된 위치에 놓이면 어떤 일이 벌어지는지 보여주었다. RFC 3028의reject는 메시지를 폐기하고 봉투 발신자에게 Message Disposition Notification을 보냈다. 이는 수신 시스템이 이미 메시지를 수신한 후에 일어날 수 있었다. 스팸이 위조된 발신자를 사용했다면, 통지는 무고한 제3자에게 가서 백스캐터를 만들었다.

RFC 5429는 이 모델을 개정했다. 그ereject동작은 가능하다면 SMTP나 LMTP에서 배달을 거부하는 것을 선호하며, 먼저 수신한 다음 나중에 통지를 구성하는 방식을 피했다. 또한 거절이 암묵적 keep를 취소한다는 것을 명확히 하고, 중복 거절을 금지했으며, 거절을 메일을 배달하는 동작과 결합하는 것을 권장하지 않았다. 그 이유는 외관상의 일관성이 아니었다. 같은 시스템이 메시지를 저장하거나 포워딩하면서 발신자에게는 거절되었다고 말해서는 안 된다.

이 역사는 하나의 상태 단어가 숨길 수 있는 세 가지 다른 사실을 드러낸다. 스크립트는 거절을 선택할 수 있다. 실행 컴포넌트는 프로토콜 경계에서 거부할 수도 있고 다른 메커니즘이 필요할 수도 있다. 발신자는 프로토콜 응답, 나중의 보고, 또는 아무것도 관찰하지 못할 수 있다. 계층과 증거 없는 "거절됨"은 불완전하다.

ManageSieve는 이후 스크립트의 업로드, 검사, 활성화를 위한 별도의 관리 표면을 표준화했다. 이 분리가 여기서 중요하다. 스크립트는 특정 메시지를 평가하지 않고도 유효하고 활성 상태일 수 있다. 평가는 동작이 의도한 끝점에 도달하지 않았더라도 동작을 선택할 수 있다. 관리, 결정, 결과는 하나가 아니라 세 개의 기록이다.

출처

Lu Heng은 RFC 3028, RFC 5228 또는 RFC 5429를 저작하거나 보증하지 않았다. 그의 에세이는 본 기사에서 공개된 분석적 관점으로 사용되었다.