]> Pileus Git - ~andy/fetchmail/blob - fetchmail.py
First round ofmlong-delayed bug fixes.
[~andy/fetchmail] / fetchmail.py
1 #!/usr/bin/env python2
2 #
3 # Python translation of fetchmail.
4 # Reads configuration from .fetchmailpyrc rather than .fetchmailrc
5 #
6 # Features removed:
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.
10
11 VERSION = "X0.1"
12
13 import os, sys, getpass, pwd, getopt, stat
14
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)
38
39 # output noise level
40 O_SILENT        = 0     # mute, max squelch, etc.
41 O_NORMAL        = 1     # user-friendly
42 O_VERBOSE       = 2     # chatty
43 O_DEBUG         = 3     # prolix
44 O_MONITOR       = O_VERBOSE
45
46 # magic port numbers
47 SMTP_PORT       = 25
48 KPOP_PORT       = 1109
49 SIMAP_PORT      = 993
50 SPOP3_PORT      = 995
51
52 def DOTLINE(s):
53     return (s[0] == '.' and (s[1]=='\r' or s[1]=='\n' or s[1]=='\0'))
54
55 # Error classes
56 class TransactionError(Exception):
57     pass
58 class GeneralError(Exception):
59     pass
60 class ProtocolError(Exception):
61     pass
62
63 class proto_pop2:
64     "POP2 protocol methods"
65     def __init__(self, ctl):
66         name = 'POP2'
67         service = 'pop2'
68         sslservice = 'pop2'
69         port = 109
70         sslport = 109
71         peek_capable = False
72         tagged = False
73         delimited = False
74         repoll = False
75         # Internal
76         pound_arg = -1
77         equal_arg = -1
78
79     def ack(sock):
80         self.pound_arg = self.equal_arg = -1
81         buf = gen_recv(sock)
82         if buf[0] == "#":
83             pass
84         elif buf[0] == "#":
85             pound_arg = int(buf[1:])
86         elif buf[0] == '=':
87             equal_arg = int(buf[1:])
88         elif buf[0] == '-':
89             raise GeneralError()
90         else:
91             raise ProtocolError()
92         return buf
93
94     def getauth(sock, ctl):
95         shroud = ctl.password
96         status = gen_transact(sock, \
97                               "HELO %s %s" % (ctl.remotename, ctl.password))
98         shroud = None
99         return status
100
101     def getrange(sock, ctl, folder):
102         if folder:
103           ok = gen_transact(sock, "FOLD %s" % folder)
104           if pound_arg == -1:
105               raise GeneralError()
106         else:
107             # We should have picked up a count of messages in the user's
108             # default inbox from the pop2_getauth() response. 
109             #
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.
115           if pound_arg == -1:
116               raise GeneralError()
117         return(pound_arg, -1, -1)
118
119     def fetch(sock, ctl, number):
120         # request nth message
121         ok = gen_transact(sock, "READ %d", number);
122         gen_send(sock, "RETR");
123         return equal_arg;
124
125     def trail(sock, ctl, number):
126         # send acknowledgement for message data
127         if ctl.keep:
128             return gen_transact(sock, "ACKS")
129         else:
130             return gen_transact(sock, "ACKD")
131
132     def logout(sock, ctl):
133         # send logout command
134         return gen_transact(sock, "QUIT")
135
136 class proto_pop3:
137     "POP3 protocol methods"
138     def __init__(self, ctl):
139         name = 'POP3'
140         service = 'pop2'
141         sslservice = 'pop2'
142         port = 110
143         sslport = 995
144         peek_capable = not ctl.fetchall
145         tagged = False
146         delimited = True
147         retry = False
148         # Internal
149         has_gssapi = FALSE
150         has_kerberos = FALSE
151         has_cram = FALSE
152         has_otp = FALSE
153         has_ssl = FALSE
154
155         # FIXME: fill in POP3 logic
156
157 class hostdata:
158     "Per-mailserver control data."
159
160     # rc file 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
182
183     # Only used under Linux
184     interface = None
185     monitor = None
186     monitor_io = 0
187     #struct interface_pair_s *interface_pair
188
189     plugin = None
190     plugout = None
191
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
200
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
206
207 class query:
208     "All the parameters of a fetchmail query."
209     # mailserver connection controls
210     server = None
211
212     # per-user data
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
218
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
230
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
257
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
270     oldsaved = []
271     newsaved = []
272     oldsavedend = []
273     lastid = None               # last Message-ID seen on this connection
274     thisid = None               # Message-ID of current message
275
276     # internal use -- per-message state
277     mimemsg = 0                 # bitmask indicating MIME body-type
278     digest = None
279
280     def dump(self):
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:
291             if self.server.skip:
292                 print "  Will not be queried when no host is specified."
293             else:
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
303                 else
304                     print "  Password = \"%s\"." % self.password
305
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)
311         else
312             sys.stdout.write("  Protocol is %s" % self.server.protocol.name)
313         if ipv6:
314             if self.server.port:
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)
318         else:
319             if self.server.port:
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")
326         print {
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]
337
338         if self.server.principal:
339             print "  Mail service principal is: %s" % self.server.principal
340         if self.use_ssl:
341             print "  SSL encrypted sessions enabled."
342         if self.sslproto:
343             print "  SSL protocol: %s." % self.sslproto;
344         if self.sslcertck:
345             print "  SSL server certificate checking enabled."
346             if self.sslcertpath:
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."
355             else
356                 print "  Selected mailboxes are: ", ", ".join(self.mailboxes)
357             flagarray = (
358                 ('fetchall', 
359                  "%s messages will be retrieved (--all %s)."
360                  "All", "Only new")
361                 ('keep', 
362                  "  Fetched messages %s be kept on the server (--keep %s)."
363                  "will", "will not")
364                 ('flush',
365                 "  Old messages %s be flushed before message retrieval (--flush %s).",
366                  "will", "will not")
367                 ('rewrite',
368                 "  Rewrite of server-local addresses is %s (norewrite %s).",
369                  "enabled", "disabled")
370                 ('stripcr',
371                 "  Carriage-return stripping is %s (stripcr %s).",
372                  "enabled", "disabled")
373                 ('forcecr',
374                 "  Carriage-return forcing is %s (forcecr %s).",
375                  "enabled", "disabled")
376                 ('pass8bits',
377                  "  Interpretation of Content-Transfer-Encoding is %s (pass8bits %s).",
378                  "enabled", "disabled")
379                 ('mimedecode',
380                  "  MIME decoding is %s (mimedecode %s).",
381                  "enabled", "disabled")
382                 ('idle',
383                  "  Idle after poll is %s (idle %s).",
384                  "enabled", "disabled")
385                 ('dropstatus',
386                  "  Nonempty Status lines will be %s (dropstatus %s)",
387                  "discarded", "kept")
388                 ('dropdelivered',
389                  "  Delivered-To lines will be %s (dropdelivered %s)",
390                  "discarded", "kept")
391                 )
392             for (attr, template, on, off) in flagarray:
393                 flag = getattr(self, att)
394                 if flag:
395                     onoff1 = on
396                     onoff2 = "on"
397                 else:
398                     onoff1 = off
399                     onoff2 = "off"
400                 print template % (onoff1, onoff2)
401             if self.limit:
402             {
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)."
413             }
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):
424             {
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)."
429             }
430         }
431         else    /* ODMR or ETRN */
432         {
433             struct idlist *idp;
434
435             print "  Domains for which mail will be fetched are:"
436             for (idp = self.domainlist; idp; idp = idp.next:
437             {
438                 printf(" %s", idp.id);
439                 if not idp.val.status.mark:
440                     print " (default)"
441             }
442             printf("");
443         }
444         if self.bsmtp:
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
448         else
449         {
450             struct idlist *idp;
451
452             if self.smtphunt:
453             {
454                 print "  Messages will be %cMTP-forwarded to:" % 
455                        self.listener);
456                 for (idp = self.smtphunt; idp; idp = idp.next:
457                 {
458                     printf(" %s", idp.id);
459                     if not idp.val.status.mark:
460                         print " (default)"
461                 }
462                 printf("");
463             }
464             if self.smtpaddress:
465                 print "  Host part of MAIL FROM line will be %s"),
466                        self.smtpaddress);
467             if self.smtpname:
468                 print "  Address to be put in RCPT TO lines shipped to SMTP will be %s"),
469                        self.smtpname);
470         }
471         if MAILBOX_PROTOCOL(ctl):
472         {
473                 if self.antispam != (struct idlist *)NULL:
474                 {
475                     struct idlist *idp;
476
477                     print "  Recognized listener spam block responses are:"
478                     for (idp = self.antispam; idp; idp = idp.next:
479                         printf(" %d", idp.val.status.num);
480                     printf("");
481                 }
482                 else if outlevel >= O_VERBOSE:
483                     print "  Spam-blocking disabled"
484         }
485         if self.preconnect:
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."
490         if self.postconnect:
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)) {
496                 if !self.localnames:
497                     print "  No localnames declared for this host."
498                 else
499                 {
500                     struct idlist *idp;
501                     int count = 0;
502
503                     for (idp = self.localnames; idp; idp = idp.next:
504                         ++count;
505
506                     if count > 1 || self.wildcard:
507                         print "  Multi-drop mode: "
508                     else
509                         print "  Single-drop mode: "
510
511                     print "%d local name(s) recognized." % count);
512                     if outlevel >= O_VERBOSE:
513                     {
514                         for (idp = self.localnames; idp; idp = idp.next:
515                             if idp.val.id2:
516                                 printf("\t%s . %s", idp.id, idp.val.id2);
517                             else
518                                 printf("\t%s", idp.id);
519                         if self.wildcard:
520                             fputs("\t*", stdout);
521                     }
522
523                     if count > 1 || self.wildcard:
524                     {
525                         print "  DNS lookup for multidrop addresses is %s."),
526                                self.server.dns ? GT_("enabled") : GT_("disabled"
527                         if self.server.dns:
528                         {
529                             print "  Server aliases will be compared with multidrop addresses by "
530                             if self.server.checkalias:
531                                 print "IP address."
532                             else
533                                 print "name."
534                         }
535                         if self.server.envelope == STRING_DISABLED:
536                             print "  Envelope-address routing is disabled"
537                         else
538                         {
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"
549                         }
550
551                         if self.server.akalist:
552                         {
553                             struct idlist *idp;
554
555                             print "  Predeclared mailserver aliases:"
556                             for (idp = self.server.akalist; idp; idp = idp.next:
557                                 printf(" %s", idp.id);
558                             putchar('');
559                         }
560                         if self.server.localdomains:
561                         {
562                             struct idlist *idp;
563
564                             print "  Local domains:"
565                             for (idp = self.server.localdomains; idp; idp = idp.next:
566                                 printf(" %s", idp.id);
567                             putchar('');
568                         }
569                     }
570                 }
571         }
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."
581 #endif
582
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."
591
592         if self.server.protocol > P_POP2 and MAILBOX_PROTOCOL(ctl):
593         {
594             if !self.oldsaved:
595                 print "  No UIDs saved from this host."
596             else
597             {
598                 struct idlist *idp;
599                 int count = 0;
600
601                 for (idp = self.oldsaved; idp; idp = idp.next:
602                     ++count;
603
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);
608             }
609         }
610
611         if self.tracepolls:
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.."
615
616         if self.properties:
617             print "  Pass-through properties \"%s\"." % self.properties
618
619
620
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 "
631                   "headers.\n"
632                   "Try 'env %s= fetchmail YOUR ARGUMENTS HERE'\n") % (injector, injector)
633             sys.exit(PS_UNDEFINED)
634
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.
637     try:
638         pwp = pwd.getpwuid(os.getuid())
639     except:
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")
646
647     cmdhelp = \
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"
662
663     # Now time to parse the command line
664     try:
665         (options, arguments) = getopt.getopt(sys.argv[1:],
666                                              "?Vcsvd:NqfakF",
667                                              ("help",
668                                               "version",
669                                               "check",
670                                               "silent",
671                                               "verbose",
672                                               "daemon",
673                                               "nodetach",
674                                               "quit",
675                                               "fetchmailrc",
676                                               "all",
677                                               "keep",
678                                               "flush",
679                                               ))
680     except getopt.GetoptError:
681         print cmdhelp
682         sys.exit(PS_SYNTAX)
683     versioninfo = checkonly = silent = nodetach = quitmode = False
684     fetchall = keep = flutch = False 
685     outlevel = O_NORMAL
686     poll_interval = -1
687     for (switch, val) in options:
688         if switch in ("-?", "--help"):
689             print cmdhelp
690             sys.exit(0)
691         elif switch in ("-V", "--version"):
692             versioninfo = True
693         elif switch in ("-c", "--check"):
694             checkonly = True
695         elif switch in ("-s", "--silent"):
696             outlevel = O_SILENT
697         elif switch in ("-v", "--verbose"):
698             if outlevel == O_VERBOSE:
699                 outlevel = O_DEBUG
700             else:
701                 outlevel = O_VERBOSE
702         elif switch in ("-d", "--daemon"):
703             poll_interval = int(val)
704         elif switch in ("-N", "--nodetach"):
705             outlevel = O_SILENT
706         elif switch in ("-q", "--quitmode"):
707             quitmode = True
708         elif switch in ("-f", "--fetchmailrc"):
709             rcfile = val
710         elif switch in ("-a", "--all"):
711             fetchall = True
712         elif switch in ("-k", "--keep"):
713             keep = True
714         elif switch in ("-F", "--flush"):
715             flush = True
716
717         if versioninfo:
718             print "This is fetchmail release", VERSION
719             os.system("uname -a")
720
721         # avoid parsing the config file if all we're doing is killing a daemon
722         fetchmailrc = {}
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.
730                 try:
731                     st = os.lstat(rcfile)
732                 except IOError:
733                     sys.exit(PS_IOERR)
734                 if not versioninfo:
735                     if not stat.S_ISREG(st.st_mode):
736                             print >>sys.stderr, \
737                                   "File %s must be a regular file." % pathname;
738                             sys.exit(PS_IOERR);
739
740                     if st.st_mode & 0067:
741                             print >>sys.stderr, \
742                                   "File %s must have no more than -rwx--x--- (0710) permissions." % pathname;
743                             sys.exit(PS_IOERR);
744             # time to read the configuration
745             if rcfile == '-':
746                 ifp = sys.stdin
747             elif os.path.exists(rcfile):
748                 ifp = file(rcfile)
749             try:
750                 exec ifp in globals()
751             except SyntaxError:
752                 print >>sys.stderr, \
753                       "File %s is ill-formed." % pathname;
754                 sys.exit(PS_SYNTAX);
755             ifp.close()
756             # generate a default configuration if user did not supply one
757             if not fetchmailrc:
758                 fetchmailrc = {
759                     'poll_interval': 300,
760                     "logfile": None,
761                     "idfile": idfile,
762                     "postmaster": "esr",
763                     'bouncemail': True,
764                     'spambounce': False,
765                     "properties": "",
766                     'invisible': False,
767                     'showdots': False,
768                     'syslog': False,
769                     'servers': []
770                     }
771                 for site in arguments:
772                     fetchmailrc['servers'].append({
773                         "pollname" : site,
774                         'active' : False,
775                         "via" : None,
776                         "protocol" : "IMAP",
777                         'port' : 0,
778                         'timeout' : 300,
779                         'interval' : 0,
780                         "envelope" : "Received",
781                         'envskip' : 0,
782                         "qvirtual" : None,
783                         "auth" : "any",
784                         'dns' : True,
785                         'uidl' : False,
786                         "aka" : [],
787                         "localdomains" : [],
788                         "interface" : None,
789                         "monitor" : None,
790                         "plugin" : None,
791                         "plugout" : None,
792                         "principal" : None,
793                         'tracepolls' : False,
794                         'users' :  [
795                             {
796                                 "remote" : user,
797                                 "password" : None,
798                                 'localnames' : [user],
799                                 'fetchall' : False,
800                                 'keep' : False,
801                                 'flush' : False,
802                                 'rewrite' : True,
803                                 'stripcr' : True,
804                                 'forcecr' : False,
805                                 'pass8bits' : False,
806                                 'dropstatus' : False,
807                                 'dropdelivered' : False,
808                                 'mimedecode' : False,
809                                 'idle' : False,
810                                 "mda" : "/usr/bin/procmail -d %T",
811                                 "bsmtp" : None,
812                                 'lmtp' : False,
813                                 "preconnect" : None,
814                                 "postconnect" : None,
815                                 'limit' : 0,
816                                 'warnings' : 3600,
817                                 'fetchlimit' : 0,
818                                 'batchlimit' : 0,
819                                 'expunge' : 0,
820                                 "properties" : None,
821                                 "smtphunt" : ["localhost"],
822                                 "fetchdomains" : [],
823                                 "smtpaddress" : None,
824                                 "smtpname" : None,
825                                 'antispam' : '',
826                                 "mailboxes" : [],
827                             }
828                             ]
829                     })
830             if poll_interval != -1: 
831                 fetchmailrc['poll_interval'] = poll_interval
832             # now turn the configuration into control structures
833