ACL: control = queue/first_pass_route
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 26 Jan 2020 14:02:31 +0000 (14:02 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 26 Jan 2020 16:35:55 +0000 (16:35 +0000)
13 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/acl.c
src/src/daemon.c
src/src/transports/smtp.c
test/confs/0599 [new file with mode: 0644]
test/log/0055
test/log/0544
test/log/0599 [new file with mode: 0644]
test/scripts/0000-Basic/0599 [new file with mode: 0644]
test/stderr/0055
test/stderr/0544
test/stdout/0599 [new file with mode: 0644]

index 3bea313..68ad169 100644 (file)
@@ -4185,6 +4185,7 @@ forces queueing.
 .vitem &%-odqs%&
 .oindex "&%-odqs%&"
 .cindex "SMTP" "delaying delivery"
+.cindex "first pass routing"
 This option is a hybrid between &%-odb%&/&%-odi%& and &%-odq%&.
 However, like &%-odb%& and &%-odi%&, this option has no effect if
 &%queue_only_override%& is false and one of the queueing options in the
@@ -4503,6 +4504,7 @@ appear in the correct order. Each flag is described in a separate item below.
 .cindex "queue" "double scanning"
 .cindex "queue" "routing"
 .cindex "routing" "whole queue before delivery"
+.cindex "first pass routing"
 An option starting with &%-qq%& requests a two-stage queue run. In the first
 stage, the queue is scanned as if the &%queue_smtp_domains%& option matched
 every domain. Addresses are routed, local deliveries happen, but no remote
@@ -16766,6 +16768,7 @@ an expansion depending on the &$queue_name$& variable.
 .option queue_smtp_domains main "domain list&!!" unset
 .cindex "queueing incoming messages"
 .cindex "message" "queueing remote deliveries"
+.cindex "first pass routing"
 When this option is set, a delivery process is started whenever a message is
 received, routing is performed, and local deliveries take place.
 However, if any SMTP deliveries are required for domains that match
@@ -30717,16 +30720,32 @@ response to an EHLO command. Therefore, it should normally appear in an ACL
 controlled by &%acl_smtp_connect%& or &%acl_smtp_helo%&. See also
 &%pipelining_advertise_hosts%&.
 
+.new
+.vitem &*control&~=&~queue/*&<&'options'&>*
 .vitem &*control&~=&~queue_only*&
+.oindex "&%queue%&"
 .oindex "&%queue_only%&"
 .cindex "queueing incoming messages"
+.cindex queueing "forcing in ACL"
+.cindex "first pass routing"
 This control is permitted only for the MAIL, RCPT, DATA, and non-SMTP ACLs, in
 other words, only when a message is being received. If the message is accepted,
 it is placed on Exim's queue and left there for delivery by a subsequent queue
-runner. No immediate delivery process is started. In other words, it has the
-effect as the &%queue_only%& global option. However, the control applies only
-to the current message, not to any subsequent ones that may be received in the
-same SMTP connection.
+runner.
+If used with no options set,
+no immediate delivery process is started. In other words, it has the
+effect as the &%queue_only%& global option or &'-odq'& command-line option.
+
+If the &'first_pass_route'& option is given then
+the behaviour is like the command-line &'-oqds'& option;
+a delivery process is started which stops short of making
+any SMTP delivery.  The benefit is that the hints database will be updated for
+the message being waiting for a specific host, and a later queue run will be
+able to send all such messages on a single connection.
+
+The control only applies to the current message, not to any subsequent ones that
+ may be received in the same SMTP connection.
+.wen
 
 .vitem &*control&~=&~submission/*&<&'options'&>
 .cindex "message" "submission"
@@ -36859,6 +36878,7 @@ delivered immediately.
 .cindex "SMTP" "passed connection"
 .cindex "SMTP" "multiple deliveries"
 .cindex "multiple SMTP deliveries"
+.cindex "first pass routing"
 Mail waiting to be sent from an intermittently connected host will probably
 not have been routed, because without a connection DNS lookups are not
 possible. This means that if a normal queue run is done at connection time,
index 8ae95a7..3542c82 100644 (file)
@@ -33,6 +33,9 @@ Version 4.94
  8. As an exerimental feature, the dovecot authenticatino driver supports inet
     sockets.  Previously it was unix-domain sockets only.
 
+ 9. The ACL control "queue_only" can also be spelled "queue", and now takes an
+    option "first_pass_route" to do the same as a "-odqs" on the command line.
+
 
 Version 4.93
 ------------
index 938bea7..952195d 100644 (file)
@@ -366,6 +366,7 @@ enum {
   CONTROL_NO_MULTILINE,
   CONTROL_NO_PIPELINING,
 
+  CONTROL_QUEUE,
   CONTROL_QUEUE_ONLY,
   CONTROL_SUBMISSION,
   CONTROL_SUPPRESS_LOCAL_FIXUPS,
@@ -502,8 +503,16 @@ static control_def controls_list[] = {
          ACL_BIT_NOTSMTP | ACL_BIT_NOTSMTP_START
   },
 
+[CONTROL_QUEUE] =
+  { US"queue",                 TRUE,
+         (unsigned)
+         ~(ACL_BIT_MAIL | ACL_BIT_RCPT |
+           ACL_BIT_PREDATA | ACL_BIT_DATA |
+           // ACL_BIT_PRDR|    /* Not allow one user to freeze for all */
+           ACL_BIT_NOTSMTP | ACL_BIT_MIME)
+  },
 [CONTROL_QUEUE_ONLY] =
-  { US"queue_only",              FALSE,
+  { US"queue_only",            TRUE,
          (unsigned)
          ~(ACL_BIT_MAIL | ACL_BIT_RCPT |
            ACL_BIT_PREDATA | ACL_BIT_DATA |
@@ -3158,9 +3167,16 @@ for (; cb; cb = cb->next)
          cancel_cutthrough_connection(TRUE, US"item frozen");
          break;
 
+       case CONTROL_QUEUE:
        case CONTROL_QUEUE_ONLY:
          f.queue_only_policy = TRUE;
          cancel_cutthrough_connection(TRUE, US"queueing forced");
+         while (*p == '/')
+           if (Ustrncmp(p, "/first_pass_route", 17) == 0)
+             {
+             p += 17;
+             f.queue_smtp = TRUE;
+             }
          break;
 
        case CONTROL_SUBMISSION:
index 61371f5..42e291c 100644 (file)
@@ -640,7 +640,8 @@ if (pid == 0)
     If we are not root, we have to re-exec exim unless deliveries are being
     done unprivileged. */
 
-    else if (!f.queue_only_policy && !f.deliver_freeze)
+    else if (  (!f.queue_only_policy || f.queue_smtp)
+            && !f.deliver_freeze)
       {
       pid_t dpid;
 
index 6559d41..b748b76 100644 (file)
@@ -4873,10 +4873,12 @@ retry_non_continued:
              &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
        )
       {
+      DEBUG(D_transport) debug_printf("first-pass routing only\n");
       expired = FALSE;
       for (address_item * addr = addrlist; addr; addr = addr->next)
         if (addr->transport_return == DEFER)
-         addr->message = US"domain matches queue_smtp_domains, or -odqs set";
+         addr->message = US"first-pass only routing due to -odqs, "
+                           "queue_smtp_domains or control=queue";
       continue;      /* With next host */
       }
 
diff --git a/test/confs/0599 b/test/confs/0599
new file mode 100644 (file)
index 0000000..5466712
--- /dev/null
@@ -0,0 +1,54 @@
+# Exim test configuration 0599
+# control = queue/first_pass_route
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+domainlist local_domains = @
+
+acl_smtp_rcpt = check_rcpt
+
+trusted_users = CALLER
+queue_run_in_order
+
+
+# ----- ACL -----
+
+begin acl
+
+check_rcpt:
+  accept       senders =       HOSTIPV4
+
+  accept       local_parts =   ^queue_only.*
+               control =       queue_only
+
+  accept       local_parts =   ^first_pass_route.*
+               control =       queue/first_pass_route
+
+# ----- Routers -----
+
+begin routers
+
+discard_remote_source:
+  driver =     redirect
+  condition =  ${if !eq {$sender_host_address}{127.0.0.1}}
+  data =       :blackhole:
+
+outbound:
+  driver =     accept
+  transport =  smtp
+
+
+# ----- Transports -----
+
+begin transports
+
+smtp:
+  driver =     smtp
+  hosts =      HOSTIPV4
+  port =       PORT_D
+  allow_localhost
+
+# End
index a3f151d..51d31aa 100644 (file)
@@ -19,7 +19,7 @@
 1999-03-02 09:44:33 10HmbB-0005vi-00 *> xxx@ten-1.test.ex R=lookuphost T=smtp H=ten-1.test.ex [V4NET.0.0.1] C="delivery bypassed by -N option"
 1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
 1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbC-0005vi-00 == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+1999-03-02 09:44:33 10HmbC-0005vi-00 == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
 1999-03-02 09:44:33 Start queue run: pid=pppp
 1999-03-02 09:44:33 10HmbC-0005vi-00 *> xxx@ten-2.test.ex R=lookuphost T=smtp H=ten-2.test.ex [V4NET.0.0.2] C="delivery bypassed by -N option"
 1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
@@ -33,7 +33,7 @@
 1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
 1999-03-02 09:44:33 End queue run: pid=pppp -qq
 1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmbF-0005vi-00 == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+1999-03-02 09:44:33 10HmbF-0005vi-00 == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
 1999-03-02 09:44:33 10HmbF-0005vi-00 *> xxx@ten-2.test.ex R=lookuphost T=smtp H=ten-2.test.ex [V4NET.0.0.2] C="delivery bypassed by -N option"
 1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
 1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.ex U=CALLER P=local S=sss
index 9d519bb..a8361a1 100644 (file)
@@ -1,3 +1,3 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain1.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
-1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain2.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain1.ex R=smarthost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain2.ex R=smarthost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
diff --git a/test/log/0599 b/test/log/0599
new file mode 100644 (file)
index 0000000..cef4d74
--- /dev/null
@@ -0,0 +1,45 @@
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => queue_only_1@test.ex R=outbound T=smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => queue_only_2@test.ex R=outbound T=smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <queue_only_1@test.ex> R=discard_remote_source
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <queue_only_2@test.ex> R=discard_remote_source
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => first_pass_route_1@test.ex R=outbound T=smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbD-0005vi-00 => first_pass_route_2@test.ex R=outbound T=smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]* C="250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=(me) [127.0.0.1] P=smtp S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=(me) [127.0.0.1] P=smtp S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@test.ex H=the.local.host.name [ip4.ip4.ip4.ip4] P=esmtp S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@test.ex H=the.local.host.name [ip4.ip4.ip4.ip4] P=esmtp S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@test.ex H=(me) [127.0.0.1] P=smtp S=sss
+1999-03-02 09:44:33 10HmbB-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@test.ex H=(me) [127.0.0.1] P=smtp S=sss
+1999-03-02 09:44:33 10HmbD-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbB-0005vi-00 == first_pass_route_1@test.ex R=outbound T=smtp defer (0): SMTP delivery explicitly queued
+1999-03-02 09:44:33 10HmbD-0005vi-00 == first_pass_route_2@test.ex R=outbound T=smtp defer (0): SMTP delivery explicitly queued
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@test.ex H=the.local.host.name [ip4.ip4.ip4.ip4] P=esmtp S=sss
+1999-03-02 09:44:33 10HmbC-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <first_pass_route_1@test.ex> R=discard_remote_source
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@test.ex H=the.local.host.name [ip4.ip4.ip4.ip4] P=esmtp S=sss
+1999-03-02 09:44:33 10HmbE-0005vi-00 no immediate delivery: queued by ACL
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <first_pass_route_2@test.ex> R=discard_remote_source
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
diff --git a/test/scripts/0000-Basic/0599 b/test/scripts/0000-Basic/0599
new file mode 100644 (file)
index 0000000..aa712de
--- /dev/null
@@ -0,0 +1,76 @@
+# control = queue/first_pass_route
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# First, control=queue_only - we expact the queue run to make separate connections
+client 127.0.0.1 PORT_D
+??? 220
+HELO me
+??? 250
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<queue_only_1@test.ex>
+??? 250
+DATA
+??? 354
+Subject: 1
+.
+??? 250
+RSET
+??? 250
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<queue_only_2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: 2
+.
+??? 250
+QUIT
+****
+exim -q
+****
+exim -q
+****
+#
+# Second, control=queue/first_pass_route - we expect the queue run to make only one
+# connection, and put both messages over it
+client 127.0.0.1 PORT_D
+??? 220
+HELO me
+??? 250
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<first_pass_route_1@test.ex>
+??? 250
+DATA
+??? 354
+Subject: 1
+.
+??? 250
+RSET
+??? 250
+MAIL FROM:<CALLER@test.ex>
+??? 250
+RCPT TO:<first_pass_route_2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: 2
+.
+??? 250
+QUIT
+****
+# give time for the background routing in the exim daemon to complete
+sleep 1
+# then run the queue
+exim -q
+****
+exim -q
+****
+#
+#
+killdaemon
+no_msglog_check
index c9729ad..e3789a4 100644 (file)
@@ -28,7 +28,7 @@ LOG: MAIN
   <= CALLER@myhost.ex U=CALLER P=local S=sss
 delivering 10HmbC-0005vi-00
 LOG: MAIN
-  == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+  == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
 LOG: queue_run MAIN
   Start queue run: pid=pppp
 delivering 10HmbC-0005vi-00 (queue run pid ppppp)
@@ -58,7 +58,7 @@ LOG: MAIN
   <= CALLER@myhost.ex U=CALLER P=local S=sss
 delivering 10HmbF-0005vi-00
 LOG: MAIN
-  == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+  == xxx@ten-2.test.ex R=lookuphost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
 delivering 10HmbF-0005vi-00
 LOG: MAIN
   *> xxx@ten-2.test.ex R=lookuphost T=smtp H=ten-2.test.ex [V4NET.0.0.2] C="delivery bypassed by -N option"
index 4f4e07b..026c286 100644 (file)
@@ -130,9 +130,9 @@ dropping to exim gid; retaining priv uid
  ╰─────result: domain1.ex
             ╰──(tainted)
 LOG: MAIN
-  == userx@domain1.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+  == userx@domain1.ex R=smarthost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
 LOG: MAIN
-  == userx@domain2.ex R=smarthost T=smtp defer (-1): domain matches queue_smtp_domains, or -odqs set
+  == userx@domain2.ex R=smarthost T=smtp defer (-1): first-pass only routing due to -odqs, queue_smtp_domains or control=queue
  ╭considering: ${if or {{ !eq{$h_list-id:$h_list-post:$h_list-subscribe:}{} }{ match{$h_precedence:}{(?i)bulk|list|junk} }{ match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }} {no}{yes}}
   ╭considering: $h_list-id:$h_list-post:$h_list-subscribe:}{} }{ match{$h_precedence:}{(?i)bulk|list|junk} }{ match{$h_auto-submitted:}{(?i)auto-generated|auto-replied} }} {no}{yes}}
   ├──expanding: $h_list-id:$h_list-post:$h_list-subscribe:
diff --git a/test/stdout/0599 b/test/stdout/0599
new file mode 100644 (file)
index 0000000..ee9dd95
--- /dev/null
@@ -0,0 +1,74 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> HELO me
+??? 250
+<<< 250 the.local.host.name Hello me [127.0.0.1]
+>>> MAIL FROM:<CALLER@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<queue_only_1@test.ex>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: 1
+>>> .
+??? 250
+<<< 250 OK id=10HmaX-0005vi-00
+>>> RSET
+??? 250
+<<< 250 Reset OK
+>>> MAIL FROM:<CALLER@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<queue_only_2@test.ex>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: 2
+>>> .
+??? 250
+<<< 250 OK id=10HmaZ-0005vi-00
+>>> QUIT
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> HELO me
+??? 250
+<<< 250 the.local.host.name Hello me [127.0.0.1]
+>>> MAIL FROM:<CALLER@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<first_pass_route_1@test.ex>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: 1
+>>> .
+??? 250
+<<< 250 OK id=10HmbB-0005vi-00
+>>> RSET
+??? 250
+<<< 250 Reset OK
+>>> MAIL FROM:<CALLER@test.ex>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<first_pass_route_2@test.ex>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: 2
+>>> .
+??? 250
+<<< 250 OK id=10HmbD-0005vi-00
+>>> QUIT
+End of script