3 # Python translation of fetchmail.
4 # Reads configuration from .fetchmailpyrc rather than .fetchmailrc
7 # 1. Support for multiple usernames per UID.
8 # 2. Repolling on a changed rc file.
9 # 3. It's no longer possible to specify site parameters from the command line.
13 import os, sys, getpass, pwd, getopt, stat
15 # fetchmail return status codes
16 PS_SUCCESS = 0 # successful receipt of messages
17 PS_NOMAIL = 1 # no mail available
18 PS_SOCKET = 2 # socket I/O woes
19 PS_AUTHFAIL = 3 # user authorization failed
20 PS_PROTOCOL = 4 # protocol violation
21 PS_SYNTAX = 5 # command-line syntax error
22 PS_IOERR = 6 # bad permissions on rc file
23 PS_ERROR = 7 # protocol error
24 PS_EXCLUDE = 8 # client-side exclusion error
25 PS_LOCKBUSY = 9 # server responded lock busy
26 PS_SMTP = 10 # SMTP error
27 PS_DNS = 11 # fatal DNS error
28 PS_BSMTP = 12 # output batch could not be opened
29 PS_MAXFETCH = 13 # poll ended by fetch limit
30 PS_SERVBUSY = 14 # server is busy
31 PS_IDLETIMEOUT = 15 # timeout on imap IDLE
32 # leave space for more codes
33 PS_UNDEFINED = 23 # something I hadn't thought of
34 PS_TRANSIENT = 24 # transient failure (internal use)
35 PS_REFUSED = 25 # mail refused (internal use)
36 PS_RETAINED = 26 # message retained (internal use)
37 PS_TRUNCATED = 27 # headers incomplete (internal use)
40 O_SILENT = 0 # mute, max squelch, etc.
41 O_NORMAL = 1 # user-friendly
42 O_VERBOSE = 2 # chatty
53 return (s[0] == '.' and (s[1]=='\r' or s[1]=='\n' or s[1]=='\0'))
56 class TransactionError(Exception):
58 class GeneralError(Exception):
60 class ProtocolError(Exception):
64 "POP2 protocol methods"
65 def __init__(self, ctl):
80 self.pound_arg = self.equal_arg = -1
85 pound_arg = int(buf[1:])
87 equal_arg = int(buf[1:])
94 def getauth(sock, ctl):
96 status = gen_transact(sock, \
97 "HELO %s %s" % (ctl.remotename, ctl.password))
101 def getrange(sock, ctl, folder):
103 ok = gen_transact(sock, "FOLD %s" % folder)
107 # We should have picked up a count of messages in the user's
108 # default inbox from the pop2_getauth() response.
110 # Note: this logic only works because there is no way to select
111 # both the unnamed folder and named folders within a single
112 # fetchmail run. If that assumption ever becomes invalid, the
113 # pop2_getauth code will have to stash the pound response away
114 # explicitly in case it gets stepped on.
117 return(pound_arg, -1, -1)
119 def fetch(sock, ctl, number):
120 # request nth message
121 ok = gen_transact(sock, "READ %d", number);
122 gen_send(sock, "RETR");
125 def trail(sock, ctl, number):
126 # send acknowledgement for message data
128 return gen_transact(sock, "ACKS")
130 return gen_transact(sock, "ACKD")
132 def logout(sock, ctl):
133 # send logout command
134 return gen_transact(sock, "QUIT")
137 "POP3 protocol methods"
138 def __init__(self, ctl):
144 peek_capable = not ctl.fetchall
155 # FIXME: fill in POP3 logic
158 "Per-mailserver control data."
161 pollname = None # poll label of host
162 via = None # "true" server name if non-NULL
163 akalist = [] # server name first, then akas
164 localdomains = [] # list of pass-through domains
165 protocol = None # protocol type
166 netsec = None # IPv6 security request
167 port = None # TCP/IP service port number (name in IPV6)
168 interval = 0 # cycles to skip between polls
169 authenticate = 'password' # authentication mode to try
170 timeout = 300 # inactivity timout in seconds
171 envelope = None # envelope address list header
172 envskip = 0 # skip to numbered envelope header
173 qvirtual = None # prefix removed from local user id
174 skip = False # suppress poll in implicit mode?
175 dns = True # do DNS lookup on multidrop?
176 uidl = False # use RFC1725 UIDLs?
177 sdps = False # use Demon Internet SDPS *ENV
178 checkalias = False # resolve aliases by comparing IPs?
179 principal = None # Kerberos principal for mail service
180 esmtp_name = None # ESMTP AUTH information
181 esmtp_password = None
183 # Only used under Linux
187 #struct interface_pair_s *interface_pair
192 # computed for internal use
193 base_protocol = None # relevant protocol method table
194 poll_count = 0 # count of polls so far
195 queryname = None # name to attempt DNS lookup on
196 truename = None # "true name" of server host
197 trueaddr = None # IP address of truename, as char
198 lead_server = None # ptr to lead query for this server
199 esmtp_options = [] # ESMTP option list
201 def is_mailbox_protocol(self):
202 # We need to distinguish between mailbox and mailbag protocols.
203 # Under a mailbox protocol we're pulling mail for a speecific user.
204 # Under a mailbag protocol we're fetching mail for an entire domain.
205 return self.protocol != proto_etrn
208 "All the parameters of a fetchmail query."
209 # mailserver connection controls
213 localnames = [] # including calling user's name
214 wildcard = False # should unmatched names be passed through
215 remotename = None # remote login name to use
216 password = None # remote password to use
217 mailboxes = [] # list of mailboxes to check
219 # per-forwarding-target data
220 smtphunt = [] # list of SMTP hosts to try forwarding to
221 domainlist = [] # domainlist to fetch from
222 smtpaddress = None # address to force in RCPT TO
223 smtpname = None # full RCPT TO name, including domain
224 antispam = [] # list of listener's antispam response
225 mda = None # local MDA to pass mail to
226 bsmtp = None # BSMTP output file
227 listener = 'SMTP' # what's the listener's wire protocol?
228 preconnect = None # pre-connection command to execute
229 postconnect = None # post-connection command to execute
231 # per-user control flags
232 keep = False # if TRUE, leave messages undeleted
233 fetchall = False # if TRUE, fetch all (not just unseen)
234 flush = False # if TRUE, delete messages already seen
235 rewrite = False # if TRUE, canonicalize recipient addresses
236 stripcr = False # if TRUE, strip CRs in text
237 forcecr = False # if TRUE, force CRs before LFs in text
238 pass8bits = False # if TRUE, ignore Content-Transfer-Encoding
239 dropstatus = False # if TRUE, drop Status lines in mail
240 dropdelivered = False # if TRUE, drop Delivered-To lines in mail
241 mimedecode = False # if TRUE, decode MIME-armored messages
242 idle = False # if TRUE, idle after each poll
243 limit = 0 # limit size of retrieved messages
244 warnings = 3600 # size warning interval
245 fetchlimit = 0 # max # msgs to get in single poll
246 batchlimit = 0 # max # msgs to pass in single SMTP session
247 expunge = 1 # max # msgs to pass between expunges
248 use_ssl = False # use SSL encrypted session
249 sslkey = None # optional SSL private key file
250 sslcert = None # optional SSL certificate file
251 sslproto = None # force usage of protocol (ssl2|ssl3|tls1) - defaults to ssl23
252 sslcertpath = None # Trusted certificate directory for checking the server cert
253 sslcertck = False # Strictly check the server cert.
254 sslfingerprint = None # Fingerprint to check against
255 properties = [] # passthrough properties for extensions
256 tracepolls = False # if TRUE, add poll trace info to Received
258 # internal use -- per-poll state
259 active = False # should we actually poll this server?
260 destaddr = None # destination host for this query
261 errcount = 0 # count transient errors in last pass
262 authfailcount = 0 # count of authorization failures
263 wehaveauthed = 0 # We've managed to logon at least once!
264 wehavesentauthnote = 0 # We've sent an authorization failure note
265 wedged = 0 # wedged by auth failures or timeouts?
266 smtphost = None # actual SMTP host we connected to
267 smtp_socket = -1 # socket descriptor for SMTP connection
268 uid = 0 # UID of user to deliver to
269 skipped = [] # messages skipped on the mail server
273 lastid = None # last Message-ID seen on this connection
274 thisid = None # Message-ID of current message
276 # internal use -- per-message state
277 mimemsg = 0 # bitmask indicating MIME body-type
281 print "Options for retrieving from %s@%s:" \
282 % (self.remotename, self.server.pollname)
283 if self.server.via and self.server.server.is_mailbox_protocol():
284 print " Mail will be retrieved via %s" % self.server.via
285 if self.server.interval:
286 print " Poll of this server will occur every %d intervals." \
287 % self.server.interval;
288 if self.server.truename:
289 print " True name of server is %s." % self.server.truename
290 if self.server.skip || outlevel >= O_VERBOSE:
292 print " Will not be queried when no host is specified."
294 print " Will not be queried when no host is specified."
295 if self.server.authenticate not in ('KERBEROS', 'GSSAPI', 'SSH'):
296 if not self.password:
297 print " Password will be prompted for."
298 else if outlevel >= O_VERBOSE:
299 if self.server.protocol == proto_apop:
300 print " APOP secret = \"%s\"." % self.password
301 elif self.server.protocol == proto_rpop:
302 print " RPOP id = \"%s\"." % self.password
304 print " Password = \"%s\"." % self.password
306 if self.server.protocol == proto_pop3 \
307 and self.server.port == KPOP_PORT \
308 and self.server.authenticate.startswith("Kerberos"):
309 sys.stdout.write(" Protocol is KPOP with %s authentication" \
310 % self.server.authenticate)
312 sys.stdout.write(" Protocol is %s" % self.server.protocol.name)
315 sys.stdout.write(" (using service %s)" % self.server.port)
316 if (self.server.netsec)
317 sys.stdout.write(" (using network security options %s)" % self.server.netsec)
320 sys.stdout.write(" (using port %d)" % self.server.port)
321 else if outlevel >= O_VERBOSE:
322 sys.stdout.write(" (using default port)")
323 if self.server.uidl and self.server.is_mailbox.protocol())
324 sys.stdout.write(" (forcing UIDL use)")
325 sys.stdout.write("\n")
327 None : " All available authentication methods will be tried.",
328 'password' : " Password authentication will be forced.",
329 'NTLM' : " NTLM authentication will be forced.",
330 'OTP' : " OTP authentication will be forced.",
331 'CRAM-MD5' " CRAM-MD5 authentication will be forced.",
332 'GSSAPI' : " GSSAPI authentication will be forced.",
333 'Kerberos V4' : " Kerberos V4 authentication will be forced.",
334 'Kerberos V5' : " Kerberos V5 authentication will be forced.",
335 'ssh' : " End-to-end encryption will be assumed.",
336 }[self.server.authenticate]
338 if self.server.principal:
339 print " Mail service principal is: %s" % self.server.principal
341 print " SSL encrypted sessions enabled."
343 print " SSL protocol: %s." % self.sslproto;
345 print " SSL server certificate checking enabled."
347 print " SSL trusted certificate directory: %s" % self.sslcertpath;
348 if self.sslfingerprint:
349 print " SSL key fingerprint (checked against the server key): %s" % self.sslfingerprint;
350 if self.server.timeout > 0:
351 print " Server nonresponse timeout is %d seconds" % self.server.timeout;
352 if self.server.is_mailbox_protocol():
353 if not self.mailboxes.id:
354 print " Default mailbox selected."
356 print " Selected mailboxes are: ", ", ".join(self.mailboxes)
359 "%s messages will be retrieved (--all %s)."
362 " Fetched messages %s be kept on the server (--keep %s)."
365 " Old messages %s be flushed before message retrieval (--flush %s).",
368 " Rewrite of server-local addresses is %s (norewrite %s).",
369 "enabled", "disabled")
371 " Carriage-return stripping is %s (stripcr %s).",
372 "enabled", "disabled")
374 " Carriage-return forcing is %s (forcecr %s).",
375 "enabled", "disabled")
377 " Interpretation of Content-Transfer-Encoding is %s (pass8bits %s).",
378 "enabled", "disabled")
380 " MIME decoding is %s (mimedecode %s).",
381 "enabled", "disabled")
383 " Idle after poll is %s (idle %s).",
384 "enabled", "disabled")
386 " Nonempty Status lines will be %s (dropstatus %s)",
389 " Delivered-To lines will be %s (dropdelivered %s)",
392 for (attr, template, on, off) in flagarray:
393 flag = getattr(self, att)
400 print template % (onoff1, onoff2)
403 if NUM_NONZERO(self.limit):
404 print " Message size limit is %d octets (--limit %d)." %
405 self.limit, self.limit);
406 else if outlevel >= O_VERBOSE:
407 print " No message size limit (--limit 0)."
408 if run.poll_interval > 0:
409 print " Message size warning interval is %d seconds (--warnings %d)." %
410 self.warnings, self.warnings);
411 else if outlevel >= O_VERBOSE:
412 print " Size warnings on every poll (--warnings 0)."
414 if NUM_NONZERO(self.fetchlimit):
415 print " Received-message limit is %d (--fetchlimit %d)."),
416 self.fetchlimit, self.fetchlimit);
417 else if outlevel >= O_VERBOSE:
418 print " No received-message limit (--fetchlimit 0)."
419 if NUM_NONZERO(self.batchlimit):
420 print " SMTP message batch limit is %d." % self.batchlimit);
421 else if outlevel >= O_VERBOSE:
422 print " No SMTP message batch limit (--batchlimit 0)."
423 if MAILBOX_PROTOCOL(ctl):
425 if NUM_NONZERO(self.expunge):
426 print " Deletion interval between expunges forced to %d (--expunge %d)." % self.expunge, self.expunge);
427 else if outlevel >= O_VERBOSE:
428 print " No forced expunges (--expunge 0)."
431 else /* ODMR or ETRN */
435 print " Domains for which mail will be fetched are:"
436 for (idp = self.domainlist; idp; idp = idp.next:
438 printf(" %s", idp.id);
439 if not idp.val.status.mark:
445 print " Messages will be appended to %s as BSMTP" % visbuf(self.bsmtp
446 else if self.mda and MAILBOX_PROTOCOL(ctl):
447 print " Messages will be delivered with \"%s\"." % visbuf(self.mda
454 print " Messages will be %cMTP-forwarded to:" %
456 for (idp = self.smtphunt; idp; idp = idp.next:
458 printf(" %s", idp.id);
459 if not idp.val.status.mark:
465 print " Host part of MAIL FROM line will be %s"),
468 print " Address to be put in RCPT TO lines shipped to SMTP will be %s"),
471 if MAILBOX_PROTOCOL(ctl):
473 if self.antispam != (struct idlist *)NULL:
477 print " Recognized listener spam block responses are:"
478 for (idp = self.antispam; idp; idp = idp.next:
479 printf(" %d", idp.val.status.num);
482 else if outlevel >= O_VERBOSE:
483 print " Spam-blocking disabled"
486 print " Server connection will be brought up with \"%s\"."),
487 visbuf(self.preconnect
488 else if outlevel >= O_VERBOSE:
489 print " No pre-connection command."
491 print " Server connection will be taken down with \"%s\"."),
492 visbuf(self.postconnect
493 else if outlevel >= O_VERBOSE:
494 print " No post-connection command."
495 if MAILBOX_PROTOCOL(ctl)) {
497 print " No localnames declared for this host."
503 for (idp = self.localnames; idp; idp = idp.next:
506 if count > 1 || self.wildcard:
507 print " Multi-drop mode: "
509 print " Single-drop mode: "
511 print "%d local name(s) recognized." % count);
512 if outlevel >= O_VERBOSE:
514 for (idp = self.localnames; idp; idp = idp.next:
516 printf("\t%s . %s", idp.id, idp.val.id2);
518 printf("\t%s", idp.id);
520 fputs("\t*", stdout);
523 if count > 1 || self.wildcard:
525 print " DNS lookup for multidrop addresses is %s."),
526 self.server.dns ? GT_("enabled") : GT_("disabled"
529 print " Server aliases will be compared with multidrop addresses by "
530 if self.server.checkalias:
535 if self.server.envelope == STRING_DISABLED:
536 print " Envelope-address routing is disabled"
539 print " Envelope header is assumed to be: %s"),
540 self.server.envelope ? self.server.envelope:GT_("Received"
541 if self.server.envskip > 1 || outlevel >= O_VERBOSE:
542 print " Number of envelope header to be parsed: %d"),
543 self.server.envskip);
544 if self.server.qvirtual:
545 print " Prefix %s will be removed from user id"),
546 self.server.qvirtual);
547 else if outlevel >= O_VERBOSE)
548 print " No prefix stripping"
551 if self.server.akalist:
555 print " Predeclared mailserver aliases:"
556 for (idp = self.server.akalist; idp; idp = idp.next:
557 printf(" %s", idp.id);
560 if self.server.localdomains:
564 print " Local domains:"
565 for (idp = self.server.localdomains; idp; idp = idp.next:
566 printf(" %s", idp.id);
572 #if defined(linux) || defined(__FreeBSD__:
573 if self.server.interface:
574 print " Connection must be through interface %s." % self.server.interface);
575 else if outlevel >= O_VERBOSE:
576 print " No interface requirement specified."
577 if self.server.monitor:
578 print " Polling loop will monitor %s." % self.server.monitor);
579 else if outlevel >= O_VERBOSE:
580 print " No monitor interface specified."
583 if self.server.plugin:
584 print " Server connections will be made via plugin %s (--plugin %s)." % self.server.plugin, self.server.plugin);
585 else if outlevel >= O_VERBOSE:
586 print " No plugin command specified."
587 if self.server.plugout:
588 print " Listener connections will be made via plugout %s (--plugout %s)." % self.server.plugout, self.server.plugout);
589 else if outlevel >= O_VERBOSE:
590 print " No plugout command specified."
592 if self.server.protocol > P_POP2 and MAILBOX_PROTOCOL(ctl):
595 print " No UIDs saved from this host."
601 for (idp = self.oldsaved; idp; idp = idp.next:
604 print " %d UIDs saved." % count);
605 if outlevel >= O_VERBOSE:
606 for (idp = self.oldsaved; idp; idp = idp.next:
607 printf("\t%s", idp.id);
612 print " Poll trace information will be added to the Received header."
613 else if outlevel >= O_VERBOSE:
614 print " No poll trace information will be added to the Received header.."
617 print " Pass-through properties \"%s\"." % self.properties
621 if __name__ == '__main__':
622 # C version queried FETCHMAILUSER, then USER, then LOGNAME.
623 # Order here is FETCHMAILUSER, LOGNAME, USER, LNAME and USERNAME.
624 user = os.getenv("FETCHMAILUSER") or getpass.getuser()
625 for injector in ("QMAILINJECT", "NULLMAILER_FLAGS"):
626 if os.getenv(injector):
627 print >>sys.stderr, \
628 ("fetchmail: The %s environment variable is set.\n"
629 "This is dangerous, as it can make qmail-inject or qmail's\n"
630 "sendmail wrapper tamper with your From or Message-ID "
632 "Try 'env %s= fetchmail YOUR ARGUMENTS HERE'\n") % (injector, injector)
633 sys.exit(PS_UNDEFINED)
635 # Figure out who calling user is and where the run-control file is.
636 # C version handled multiple usernames per PID; this doesn't.
638 pwp = pwd.getpwuid(os.getuid())
640 print >>sys.stderr, "You don't exist. Go away."
641 sys.exit(PS_UNDEFINED)
642 home = os.getenv("HOME") or pwp.pw_dir
643 fmhome = os.getenv("FETCHMAILHOME") or home
644 rcfile = os.path.join(fmhome, ".fetchmailpyrc")
645 idfile = os.path.join(fmhome, ".fetchids")
648 "usage: fetchmail [options] [server ...]\n" \
649 " Options are as follows:\n" \
650 " -?, --help display this option help\n" \
651 " -V, --version display version info\n" \
652 " -c, --check check for messages without fetching\n" \
653 " -s, --silent work silently\n" \
654 " -v, --verbose work noisily (diagnostic output)\n" \
655 " -d, --daemon run as a daemon once per n seconds\n" \
656 " -N, --nodetach don't detach daemon process\n" \
657 " -q, --quit kill daemon process\n" \
658 " -f, --fetchmailrc specify alternate run control file\n" \
659 " -a, --all retrieve old and new messages\n" \
660 " -k, --keep save new messages after retrieval\n" \
661 " -F, --flush delete old messages from server\n"
663 # Now time to parse the command line
665 (options, arguments) = getopt.getopt(sys.argv[1:],
680 except getopt.GetoptError:
683 versioninfo = checkonly = silent = nodetach = quitmode = False
684 fetchall = keep = flutch = False
687 for (switch, val) in options:
688 if switch in ("-?", "--help"):
691 elif switch in ("-V", "--version"):
693 elif switch in ("-c", "--check"):
695 elif switch in ("-s", "--silent"):
697 elif switch in ("-v", "--verbose"):
698 if outlevel == O_VERBOSE:
702 elif switch in ("-d", "--daemon"):
703 poll_interval = int(val)
704 elif switch in ("-N", "--nodetach"):
706 elif switch in ("-q", "--quitmode"):
708 elif switch in ("-f", "--fetchmailrc"):
710 elif switch in ("-a", "--all"):
712 elif switch in ("-k", "--keep"):
714 elif switch in ("-F", "--flush"):
718 print "This is fetchmail release", VERSION
719 os.system("uname -a")
721 # avoid parsing the config file if all we're doing is killing a daemon
723 if not quitmode or len(sys.argv) != 2:
724 # user probably supplied a configuration file, check security
725 if os.path.exists(rcfile):
726 # the run control file must have the same uid as the
727 # REAL uid of this process, it must have permissions
728 # no greater than 600, and it must not be a symbolic
729 # link. We check these conditions here.
731 st = os.lstat(rcfile)
735 if not stat.S_ISREG(st.st_mode):
736 print >>sys.stderr, \
737 "File %s must be a regular file." % pathname;
740 if st.st_mode & 0067:
741 print >>sys.stderr, \
742 "File %s must have no more than -rwx--x--- (0710) permissions." % pathname;
744 # time to read the configuration
747 elif os.path.exists(rcfile):
750 exec ifp in globals()
752 print >>sys.stderr, \
753 "File %s is ill-formed." % pathname;
756 # generate a default configuration if user did not supply one
759 'poll_interval': 300,
771 for site in arguments:
772 fetchmailrc['servers'].append({
780 "envelope" : "Received",
793 'tracepolls' : False,
798 'localnames' : [user],
806 'dropstatus' : False,
807 'dropdelivered' : False,
808 'mimedecode' : False,
810 "mda" : "/usr/bin/procmail -d %T",
814 "postconnect" : None,
821 "smtphunt" : ["localhost"],
823 "smtpaddress" : None,
830 if poll_interval != -1:
831 fetchmailrc['poll_interval'] = poll_interval
832 # now turn the configuration into control structures