Commit | Line | Data |
---|---|---|
c638d827 CR |
1 | # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) |
2 | # This module is free software, and you may redistribute it and/or modify | |
3 | # under the same terms as Python, so long as this copyright message and | |
4 | # disclaimer are retained in their original form. | |
5 | # | |
6 | # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR | |
7 | # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING | |
8 | # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE | |
9 | # POSSIBILITY OF SUCH DAMAGE. | |
10 | # | |
11 | # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
12 | # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
13 | # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" | |
14 | # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, | |
15 | # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
16 | # | |
17 | # $Id: roundup_mailgw.py,v 1.25 2008-08-19 01:06:01 richard Exp $ | |
18 | ||
19 | """Command-line script stub that calls the roundup.mailgw. | |
20 | """ | |
21 | __docformat__ = 'restructuredtext' | |
22 | ||
23 | # python version check | |
24 | from roundup import version_check | |
25 | from roundup import __version__ as roundup_version | |
26 | ||
27 | import sys, os, re, cStringIO, getopt, socket, netrc | |
28 | ||
29 | from roundup import mailgw | |
30 | from roundup.i18n import _ | |
31 | ||
32 | def usage(args, message=None): | |
33 | if message is not None: | |
34 | print message | |
35 | print _( | |
36 | """Usage: %(program)s [-v] [-c class] [[-C class] -S field=value]* <instance home> [method] | |
37 | ||
38 | Options: | |
39 | -v: print version and exit | |
40 | -c: default class of item to create (else the tracker's MAIL_DEFAULT_CLASS) | |
41 | -C / -S: see below | |
42 | ||
43 | The roundup mail gateway may be called in one of four ways: | |
44 | . with an instance home as the only argument, | |
45 | . with both an instance home and a mail spool file, | |
46 | . with both an instance home and a POP/APOP server account, or | |
47 | . with both an instance home and a IMAP/IMAPS server account. | |
48 | ||
49 | It also supports optional -C and -S arguments that allows you to set a | |
50 | fields for a class created by the roundup-mailgw. The default class if | |
51 | not specified is msg, but the other classes: issue, file, user can | |
52 | also be used. The -S or --set options uses the same | |
53 | property=value[;property=value] notation accepted by the command line | |
54 | roundup command or the commands that can be given on the Subject line | |
55 | of an email message. | |
56 | ||
57 | It can let you set the type of the message on a per email address basis. | |
58 | ||
59 | PIPE: | |
60 | In the first case, the mail gateway reads a single message from the | |
61 | standard input and submits the message to the roundup.mailgw module. | |
62 | ||
63 | UNIX mailbox: | |
64 | In the second case, the gateway reads all messages from the mail spool | |
65 | file and submits each in turn to the roundup.mailgw module. The file is | |
66 | emptied once all messages have been successfully handled. The file is | |
67 | specified as: | |
68 | mailbox /path/to/mailbox | |
69 | ||
70 | In all of the following the username and password can be stored in a | |
71 | ~/.netrc file. In this case only the server name need be specified on | |
72 | the command-line. | |
73 | ||
74 | The username and/or password will be prompted for if not supplied on | |
75 | the command-line or in ~/.netrc. | |
76 | ||
77 | POP: | |
78 | In the third case, the gateway reads all messages from the POP server | |
79 | specified and submits each in turn to the roundup.mailgw module. The | |
80 | server is specified as: | |
81 | pop username:password@server | |
82 | Alternatively, one can omit one or both of username and password: | |
83 | pop username@server | |
84 | pop server | |
85 | are both valid. | |
86 | ||
87 | POPS: | |
88 | Connect to a POP server over ssl. This requires python 2.4 or later. | |
89 | This supports the same notation as POP. | |
90 | ||
91 | APOP: | |
92 | Same as POP, but using Authenticated POP: | |
93 | apop username:password@server | |
94 | ||
95 | IMAP: | |
96 | Connect to an IMAP server. This supports the same notation as that of | |
97 | POP mail. | |
98 | imap username:password@server | |
99 | It also allows you to specify a specific mailbox other than INBOX using | |
100 | this format: | |
101 | imap username:password@server mailbox | |
102 | ||
103 | IMAPS: | |
104 | Connect to an IMAP server over ssl. | |
105 | This supports the same notation as IMAP. | |
106 | imaps username:password@server [mailbox] | |
107 | ||
108 | IMAPS_CRAM: | |
109 | Connect to an IMAP server over ssl using CRAM-MD5 authentication. | |
110 | This supports the same notation as IMAP. | |
111 | imaps_cram username:password@server [mailbox] | |
112 | ||
113 | """)%{'program': args[0]} | |
114 | return 1 | |
115 | ||
116 | def main(argv): | |
117 | '''Handle the arguments to the program and initialise environment. | |
118 | ''' | |
119 | # take the argv array and parse it leaving the non-option | |
120 | # arguments in the args array. | |
121 | try: | |
122 | optionsList, args = getopt.getopt(argv[1:], 'vc:C:S:', ['set=', | |
123 | 'class=']) | |
124 | except getopt.GetoptError: | |
125 | # print help information and exit: | |
126 | usage(argv) | |
127 | sys.exit(2) | |
128 | ||
129 | for (opt, arg) in optionsList: | |
130 | if opt == '-v': | |
131 | print '%s (python %s)'%(roundup_version, sys.version.split()[0]) | |
132 | return | |
133 | ||
134 | # figure the instance home | |
135 | if len(args) > 0: | |
136 | instance_home = args[0] | |
137 | else: | |
138 | instance_home = os.environ.get('ROUNDUP_INSTANCE', '') | |
139 | if not (instance_home and os.path.isdir(instance_home)): | |
140 | return usage(argv) | |
141 | ||
142 | # get the instance | |
143 | import roundup.instance | |
144 | instance = roundup.instance.open(instance_home) | |
145 | ||
146 | if hasattr(instance, 'MailGW'): | |
147 | handler = instance.MailGW(instance, optionsList) | |
148 | else: | |
149 | handler = mailgw.MailGW(instance, optionsList) | |
150 | ||
151 | # if there's no more arguments, read a single message from stdin | |
152 | if len(args) == 1: | |
153 | return handler.do_pipe() | |
154 | ||
155 | # otherwise, figure what sort of mail source to handle | |
156 | if len(args) < 3: | |
157 | return usage(argv, _('Error: not enough source specification information')) | |
158 | source, specification = args[1:3] | |
159 | ||
160 | # time out net connections after a minute if we can | |
161 | if source not in ('mailbox', 'imaps', 'imaps_cram'): | |
162 | if hasattr(socket, 'setdefaulttimeout'): | |
163 | socket.setdefaulttimeout(60) | |
164 | ||
165 | if source == 'mailbox': | |
166 | return handler.do_mailbox(specification) | |
167 | ||
168 | # the source will be a network server, so obtain the credentials to | |
169 | # use in connecting to the server | |
170 | try: | |
171 | # attempt to obtain credentials from a ~/.netrc file | |
172 | authenticator = netrc.netrc().authenticators(specification) | |
173 | username = authenticator[0] | |
174 | password = authenticator[2] | |
175 | server = specification | |
176 | # IOError if no ~/.netrc file, TypeError if the hostname | |
177 | # not found in the ~/.netrc file: | |
178 | except (IOError, TypeError): | |
179 | match = re.match(r'((?P<user>[^:]+)(:(?P<pass>.+))?@)?(?P<server>.+)', | |
180 | specification) | |
181 | if match: | |
182 | username = match.group('user') | |
183 | password = match.group('pass') | |
184 | server = match.group('server') | |
185 | else: | |
186 | return usage(argv, _('Error: %s specification not valid') % source) | |
187 | ||
188 | # now invoke the mailgw handler depending on the server handler requested | |
189 | if source.startswith('pop'): | |
190 | ssl = source.endswith('s') | |
191 | if ssl and sys.version_info<(2,4): | |
192 | return usage(argv, _('Error: a later version of python is required')) | |
193 | return handler.do_pop(server, username, password, ssl) | |
194 | elif source == 'apop': | |
195 | return handler.do_apop(server, username, password) | |
196 | elif source.startswith('imap'): | |
197 | ssl = cram = 0 | |
198 | if source.endswith('s'): | |
199 | ssl = 1 | |
200 | elif source.endswith('s_cram'): | |
201 | ssl = cram = 1 | |
202 | mailbox = '' | |
203 | if len(args) > 3: | |
204 | mailbox = args[3] | |
205 | return handler.do_imap(server, username, password, mailbox, ssl, | |
206 | cram) | |
207 | ||
208 | return usage(argv, _('Error: The source must be either "mailbox",' | |
209 | ' "pop", "pops", "apop", "imap", "imaps" or "imaps_cram')) | |
210 | ||
211 | def run(): | |
212 | sys.exit(main(sys.argv)) | |
213 | ||
214 | # call main | |
215 | if __name__ == '__main__': | |
216 | run() | |
217 | ||
218 | # vim: set filetype=python ts=4 sw=4 et si |