Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
E
email2trac
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Stefy Lanza (nextime / spora )
email2trac
Commits
3f38d993
Commit
3f38d993
authored
10 years ago
by
nextime
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
first commit
parents
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
928 additions
and
0 deletions
+928
-0
email2trac.conf
email2trac.conf
+20
-0
email2trac.py
email2trac.py
+908
-0
No files found.
email2trac.conf
0 → 100644
View file @
3f38d993
[
DEFAULT
]
project
: /
data
/
trac
/
test
debug
:
2
umask
:
022
reply_all
:
1
mailto_link
:
0
umask
:
022
email_comment
: >
email_header
:
0
trac_version
:
0
.
10
enable_syslog
:
0
alternate_notify_template
:
drop_spam
:
0
verbatim_format
:
1
strip_signature
:
1
myaddress
:
ticket
@
trac
.
tld
[
bas
]
project
: /
data
/
trac
/
bas
myaddress
:
bas
@
ticket
.
address
.
tld
This diff is collapsed.
Click to expand it.
email2trac.py
0 → 100755
View file @
3f38d993
#!/usr/bin/env python
# Copyright (C) 2002
#
# This file is part of the email2trac utils
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#
# For vi/emacs or other use tabstop=4 (vi: set ts=4)
#
"""
email2trac.py -- Email tickets to Trac.
A simple MTA filter to create Trac tickets from inbound emails.
Copyright 2005, Daniel Lundin <daniel@edgewall.com>
Copyright 2005, Edgewall Software
Changed By: Bas van der Vlies <basv@sara.nl>
Date : 13 September 2005
Descr. : Added config file and command line options, spam level
detection, reply address and mailto option. Unicode support
Changed By: Walter de Jong <walter@sara.nl>
Descr. : multipart-message code and trac attachments
Changed By: Franco (nextime) Lanza <nextime@nexlab.it>
Date: 18/01/2007
Descr. : Some semplifications on the script.
The scripts reads emails from stdin and inserts directly into a Trac database.
MIME headers are mapped as follows:
* From: => Reporter
=> CC (Optional via reply_address option)
* Subject: => Summary
* Body => Description
* Component => Can be set to SPAM via spam_level option
How to use
----------
* Create an config file:
[DEFAULT] # REQUIRED
project : /data/trac/test # REQUIRED
debug : 1 # OPTIONAL, if set print some DEBUG info
reply_address: 1 # OPTIONAL, if set then fill in ticket CC field
umask : 022 # OPTIONAL, if set then use this umask for creation of the attachments
mailto_link : 1 # OPTIONAL, if set then [mailto:<>] in description
myaddress : address@trac.tld
mailto_cc : basv@sara.nl # OPTIONAL, use this address as CC in mailto line
ticket_update: 1 # OPTIONAL, if set then check if this is an update for a ticket
trac_version : 0.9 # OPTIONAL, default is 0.10
[jouvin] # OPTIONAL project declaration, if set both fields necessary
project : /data/trac/jouvin # use -p|--project jouvin.
myaddress : ticket@jouin.tld
* default config file is : /etc/email2trac.conf
* Commandline opions:
-h | --help
-c <value> | --component=<value>
-f <config file> | --file=<config file>
-p <project name> | --project=<project name>
"""
import
os
import
sys
import
string
import
getopt
import
stat
import
time
import
email
import
email.Iterators
import
email.Header
import
re
import
urllib
import
unicodedata
import
ConfigParser
from
stat
import
*
import
mimetypes
import
syslog
import
traceback
# Some global variables
#
trac_default_version
=
0.10
m
=
None
class
TicketEmailParser
(
object
):
env
=
None
comment
=
'> '
def
__init__
(
self
,
env
,
parameters
,
version
):
self
.
env
=
env
# Database connection
#
self
.
db
=
None
# Some useful mail constants
#
self
.
author
=
None
self
.
email_addr
=
None
self
.
email_field
=
None
self
.
VERSION
=
version
if
self
.
VERSION
==
0.8
:
self
.
get_config
=
self
.
env
.
get_config
else
:
self
.
get_config
=
self
.
env
.
config
.
get
if
parameters
.
has_key
(
'umask'
):
os
.
umask
(
int
(
parameters
[
'umask'
],
8
))
if
parameters
.
has_key
(
'myaddress'
):
self
.
myaddress
=
parameters
[
'myaddress'
]
else
:
self
.
myaddress
=
None
if
parameters
.
has_key
(
'debug'
):
self
.
DEBUG
=
int
(
parameters
[
'debug'
])
else
:
self
.
DEBUG
=
0
if
parameters
.
has_key
(
'mailto_link'
):
self
.
MAILTO
=
int
(
parameters
[
'mailto_link'
])
if
parameters
.
has_key
(
'mailto_cc'
):
self
.
MAILTO_CC
=
parameters
[
'mailto_cc'
]
else
:
self
.
MAILTO_CC
=
''
else
:
self
.
MAILTO
=
0
if
parameters
.
has_key
(
'email_comment'
):
self
.
comment
=
str
(
parameters
[
'email_comment'
])
if
parameters
.
has_key
(
'email_header'
):
self
.
EMAIL_HEADER
=
int
(
parameters
[
'email_header'
])
else
:
self
.
EMAIL_HEADER
=
0
if
parameters
.
has_key
(
'alternate_notify_template'
):
self
.
notify_template
=
str
(
parameters
[
'alternate_notify_template'
])
else
:
self
.
notify_template
=
None
if
parameters
.
has_key
(
'reply_all'
):
self
.
REPLY_ALL
=
int
(
parameters
[
'reply_all'
])
else
:
self
.
REPLY_ALL
=
0
if
parameters
.
has_key
(
'ticket_update'
):
self
.
TICKET_UPDATE
=
int
(
parameters
[
'ticket_update'
])
else
:
self
.
TICKET_UPDATE
=
0
if
parameters
.
has_key
(
'drop_spam'
):
self
.
DROP_SPAM
=
int
(
parameters
[
'drop_spam'
])
else
:
self
.
DROP_SPAM
=
0
if
parameters
.
has_key
(
'verbatim_format'
):
self
.
VERBATIM_FORMAT
=
int
(
parameters
[
'verbatim_format'
])
else
:
self
.
VERBATIM_FORMAT
=
1
if
parameters
.
has_key
(
'strip_signature'
):
self
.
STRIP_SIGNATURE
=
int
(
parameters
[
'strip_signature'
])
else
:
self
.
STRIP_SIGNATURE
=
0
def
spam
(
self
,
message
):
if
message
.
has_key
(
'X-Spam-Status'
):
spamvalue
=
message
[
'X-Spam-Status'
]
.
split
(
","
)[
0
]
if
spamvalue
==
"Yes"
:
return
'Spam'
elif
message
.
has_key
(
'X-Virus-found'
):
# treat virus mails as spam
return
'Spam'
return
self
.
get_config
(
'ticket'
,
'default_component'
)
def
email_to_unicode
(
self
,
message_str
):
"""
Email has 7 bit ASCII code, convert it to unicode with the charset
that is encoded in 7-bit ASCII code and encode it as utf-8 so Trac
understands it.
"""
results
=
email
.
Header
.
decode_header
(
message_str
)
str
=
None
for
text
,
format
in
results
:
if
format
:
try
:
temp
=
unicode
(
text
,
format
)
except
UnicodeError
,
detail
:
# This always works
#
temp
=
unicode
(
text
,
'iso-8859-15'
)
except
LookupError
,
detail
:
#text = 'ERROR: Could not find charset: %s, please install' %format
#temp = unicode(text, 'iso-8859-15')
temp
=
message_str
else
:
temp
=
string
.
strip
(
text
)
temp
=
unicode
(
text
,
'iso-8859-15'
)
if
str
:
str
=
'
%
s
%
s'
%
(
str
,
temp
)
else
:
str
=
'
%
s'
%
temp
#str = str.encode('utf-8')
return
str
def
debug_attachments
(
self
,
message
):
n
=
0
for
part
in
message
.
walk
():
if
part
.
get_content_maintype
()
==
'multipart'
:
# multipart/* is just a container
print
'TD: multipart container'
continue
n
=
n
+
1
print
'TD: part
%
d: Content-Type:
%
s'
%
(
n
,
part
.
get_content_type
())
print
'TD: part
%
d: filename:
%
s'
%
(
n
,
part
.
get_filename
())
if
part
.
is_multipart
():
print
'TD: this part is multipart'
payload
=
part
.
get_payload
(
decode
=
1
)
print
'TD: payload:'
,
payload
else
:
print
'TD: this part is not multipart'
part_file
=
'/tmp/part
%
d'
%
n
print
'TD: writing part
%
d (
%
s)'
%
(
n
,
part_file
)
fx
=
open
(
part_file
,
'wb'
)
text
=
part
.
get_payload
(
decode
=
1
)
if
not
text
:
text
=
'(None)'
fx
.
write
(
text
)
fx
.
close
()
try
:
os
.
chmod
(
part_file
,
S_IRWXU
|
S_IRWXG
|
S_IRWXO
)
except
OSError
:
pass
def
email_header_txt
(
self
,
m
):
"""
Display To and CC addresses in description field
"""
str
=
''
if
m
[
'To'
]
and
len
(
m
[
'To'
])
>
0
and
m
[
'To'
]
!=
'hic@sara.nl'
:
str
=
"'''To:'''
%
s [[BR]]"
%
(
m
[
'To'
])
if
m
[
'Cc'
]
and
len
(
m
[
'Cc'
])
>
0
:
str
=
"
%
s'''Cc:'''
%
s [[BR]]"
%
(
str
,
m
[
'Cc'
])
return
self
.
email_to_unicode
(
str
)
def
set_owner
(
self
,
ticket
):
"""
Select default owner for ticket component
"""
cursor
=
self
.
db
.
cursor
()
sql
=
"SELECT owner FROM component WHERE name='
%
s'"
%
ticket
[
'component'
]
cursor
.
execute
(
sql
)
try
:
ticket
[
'owner'
]
=
cursor
.
fetchone
()[
0
]
except
TypeError
,
detail
:
ticket
[
'owner'
]
=
"UNKNOWN"
def
get_author_emailaddrs
(
self
,
message
):
"""
Get the default author name and email address from the message
"""
temp
=
self
.
email_to_unicode
(
message
[
'from'
])
#print temp.encode('utf-8')
self
.
author
,
self
.
email_addr
=
email
.
Utils
.
parseaddr
(
temp
)
# if the "from" email addr is equal to the trac sender,
# exit and do nothing!
if
self
.
email_addr
==
self
.
myaddress
:
sys
.
exit
(
0
)
#print self.author.encode('utf-8', 'replace')
# Look for email address in registered trac users
#
if
self
.
VERSION
==
0.8
:
users
=
[]
else
:
users
=
[
u
for
(
u
,
n
,
e
)
in
self
.
env
.
get_known_users
(
self
.
db
)
if
e
==
self
.
email_addr
]
if
len
(
users
)
==
1
:
self
.
email_field
=
users
[
0
]
else
:
self
.
email_field
=
self
.
email_to_unicode
(
message
[
'from'
])
def
set_reply_fields
(
self
,
ticket
,
message
):
"""
Set all the right fields for a new ticket
"""
ticket
[
'reporter'
]
=
self
.
email_field
# Put all CC-addresses in ticket CC field
#
if
self
.
REPLY_ALL
:
#tos = message.get_all('to', [])
ccs
=
message
.
get_all
(
'cc'
,
[])
addrs
=
email
.
Utils
.
getaddresses
(
ccs
)
if
not
addrs
:
return
# Remove reporter email address if notification is
# on
#
if
self
.
notification
:
try
:
addrs
.
remove
((
self
.
author
,
self
.
email_addr
))
except
ValueError
,
detail
:
pass
for
name
,
mail
in
addrs
:
# Remove the trac email2ticket address,
# and add others to mail_list
if
mail
!=
self
.
myaddress
:
try
:
mail_list
=
'
%
s,
%
s'
%
(
mail_list
,
mail
)
except
:
mail_list
=
mail
if
mail_list
:
ticket
[
'cc'
]
=
self
.
email_to_unicode
(
mail_list
)
def
save_email_for_debug
(
self
,
message
,
tempfile
=
False
):
if
tempfile
:
import
tempfile
msg_file
=
tempfile
.
mktemp
(
'.email2trac'
)
else
:
msg_file
=
'/tmp/msg.txt'
print
'TD: saving email to
%
s'
%
msg_file
fx
=
open
(
msg_file
,
'wb'
)
fx
.
write
(
'
%
s'
%
message
)
fx
.
close
()
try
:
os
.
chmod
(
msg_file
,
S_IRWXU
|
S_IRWXG
|
S_IRWXO
)
except
OSError
:
pass
def
ticket_update
(
self
,
m
):
"""
If the current email is a reply to an existing ticket, this function
will append the contents of this email to that ticket, instead of
creating a new one.
"""
if
not
m
[
'Subject'
]:
return
False
else
:
subject
=
self
.
email_to_unicode
(
m
[
'Subject'
])
TICKET_RE
=
re
.
compile
(
r"""
(?P<ticketnr>[#][0-9]+:)
"""
,
re
.
VERBOSE
)
result
=
TICKET_RE
.
search
(
subject
)
if
not
result
:
return
False
body_text
=
self
.
get_body_text
(
m
)
# Strip '#' and ':' from ticket_id
#
ticket_id
=
result
.
group
(
'ticketnr'
)
ticket_id
=
int
(
ticket_id
[
1
:
-
1
])
# Get current time
#
when
=
int
(
time
.
time
())
if
self
.
VERSION
==
0.8
:
tkt
=
Ticket
(
self
.
db
,
ticket_id
)
tkt
.
save_changes
(
self
.
db
,
self
.
author
,
body_text
,
when
)
else
:
try
:
tkt
=
Ticket
(
self
.
env
,
ticket_id
,
self
.
db
)
except
util
.
TracError
,
detail
:
return
False
tkt
.
save_changes
(
self
.
author
,
body_text
,
when
)
tkt
[
'id'
]
=
ticket_id
if
(
self
.
VERSION
==
0.9
)
or
(
self
.
VERSION
==
0.10
):
self
.
attachments
(
m
,
tkt
,
True
)
else
:
self
.
attachments
(
m
,
tkt
)
if
self
.
notification
:
self
.
notify
(
tkt
,
False
,
when
)
return
True
def
new_ticket
(
self
,
msg
):
"""
Create a new ticket
"""
tkt
=
Ticket
(
self
.
env
)
tkt
[
'status'
]
=
'new'
# Some defaults
#
tkt
[
'milestone'
]
=
self
.
get_config
(
'ticket'
,
'default_milestone'
)
tkt
[
'priority'
]
=
self
.
get_config
(
'ticket'
,
'default_priority'
)
tkt
[
'severity'
]
=
self
.
get_config
(
'ticket'
,
'default_severity'
)
tkt
[
'version'
]
=
self
.
get_config
(
'ticket'
,
'default_version'
)
if
not
msg
[
'Subject'
]:
tkt
[
'summary'
]
=
u'(geen subject)'
else
:
tkt
[
'summary'
]
=
self
.
email_to_unicode
(
msg
[
'Subject'
])
if
settings
.
has_key
(
'component'
):
tkt
[
'component'
]
=
settings
[
'component'
]
else
:
tkt
[
'component'
]
=
self
.
spam
(
msg
)
# Discard SPAM messages.
#
if
self
.
DROP_SPAM
and
(
tkt
[
'component'
]
==
'Spam'
):
# print 'This message is a SPAM. Automatic ticket insertion refused
return
False
# Set default owner for component
#
self
.
set_owner
(
tkt
)
self
.
set_reply_fields
(
tkt
,
msg
)
# produce e-mail like header
#
head
=
''
if
self
.
EMAIL_HEADER
>
0
:
head
=
self
.
email_header_txt
(
msg
)
body_text
=
self
.
get_body_text
(
msg
)
tkt
[
'description'
]
=
'
\r\n
%
s
\r\n
%
s'
\
%
(
head
,
body_text
)
when
=
int
(
time
.
time
())
if
self
.
VERSION
==
0.8
:
ticket_id
=
tkt
.
insert
(
self
.
db
)
else
:
ticket_id
=
tkt
.
insert
()
tkt
[
'id'
]
=
ticket_id
changed
=
False
comment
=
''
# Rewrite the description if we have mailto enabled
#
if
self
.
MAILTO
:
changed
=
True
comment
=
u'
\n
added mailto line
\n
'
mailto
=
self
.
html_mailto_link
(
tkt
[
'summary'
],
ticket_id
,
body_text
)
tkt
[
'description'
]
=
u'
\r\n
%
s
\r\n
%
s
%
s
\r\n
'
\
%
(
head
,
mailto
,
body_text
)
n
=
self
.
attachments
(
msg
,
tkt
)
if
n
:
changed
=
True
comment
=
'
%
s
\n
This message has
%
d attachment(s)
\n
'
%
(
comment
,
n
)
if
changed
:
if
self
.
VERSION
==
0.8
:
tkt
.
save_changes
(
self
.
db
,
self
.
author
,
comment
)
else
:
tkt
.
save_changes
(
self
.
author
,
comment
)
#print tkt.get_changelog(self.db, when)
if
self
.
notification
:
self
.
notify
(
tkt
,
True
)
#self.notify(tkt, False)
def
parse
(
self
,
fp
):
global
m
m
=
email
.
message_from_file
(
fp
)
if
not
m
:
return
if
self
.
DEBUG
>
1
:
# save the entire e-mail message text
self
.
save_email_for_debug
(
m
)
self
.
debug_attachments
(
m
)
self
.
db
=
self
.
env
.
get_db_cnx
()
self
.
get_author_emailaddrs
(
m
)
if
self
.
get_config
(
'notification'
,
'smtp_enabled'
)
in
[
'true'
]:
self
.
notification
=
1
else
:
self
.
notification
=
0
# Must we update existing tickets
#
if
self
.
TICKET_UPDATE
>
0
:
if
self
.
ticket_update
(
m
):
return
True
self
.
new_ticket
(
m
)
def
strip_signature
(
self
,
text
):
"""
Strip signature from message, inspired by Mailman software
"""
body
=
[]
for
line
in
text
.
splitlines
():
if
line
==
'-- '
:
break
body
.
append
(
line
)
return
(
'
\n
'
.
join
(
body
))
def
get_body_text
(
self
,
msg
):
"""
put the message text in the ticket description or in the changes field.
message text can be plain text or html or something else
"""
has_description
=
0
encoding
=
True
ubody_text
=
u'No plain text message'
for
part
in
msg
.
walk
():
# 'multipart/*' is a container for multipart messages
#
if
part
.
get_content_maintype
()
==
'multipart'
:
continue
if
part
.
get_content_type
()
==
'text/plain'
:
# Try to decode, if fails then do not decode
#
body_text
=
part
.
get_payload
(
decode
=
1
)
if
not
body_text
:
body_text
=
part
.
get_payload
(
decode
=
0
)
if
self
.
STRIP_SIGNATURE
:
body_text
=
self
.
strip_signature
(
body_text
)
# Get contents charset (iso-8859-15 if not defined in mail headers)
#
charset
=
part
.
get_content_charset
()
if
not
charset
:
charset
=
'iso-8859-15'
try
:
ubody_text
=
unicode
(
body_text
,
charset
)
except
UnicodeError
,
detail
:
ubody_text
=
unicode
(
body_text
,
'iso-8859-15'
)
except
LookupError
,
detail
:
ubody_text
=
'ERROR: Could not find charset:
%
s, please install'
%
(
charset
)
elif
part
.
get_content_type
()
==
'text/html'
:
ubody_text
=
'(see attachment for HTML mail message)'
else
:
ubody_text
=
'(see attachment for message)'
has_description
=
1
break
# we have the description, so break
if
not
has_description
:
ubody_text
=
'(see attachment for message)'
# A patch so that the web-interface will not update the description
# field of a ticket
#
ubody_text
=
(
'
\r\n
'
.
join
(
ubody_text
.
splitlines
()))
# If we can unicode it try to encode it for trac
# else we a lot of garbage
#
#if encoding:
# ubody_text = ubody_text.encode('utf-8')
if
self
.
VERBATIM_FORMAT
:
ubody_text
=
'{{{
\r\n
%
s
\r\n
}}}'
%
ubody_text
else
:
ubody_text
=
'
%
s'
%
ubody_text
return
ubody_text
def
notify
(
self
,
tkt
,
new
=
True
,
modtime
=
0
):
"""
A wrapper for the TRAC notify function. So we can use templates
"""
if
tkt
[
'component'
]
==
'Spam'
:
return
try
:
# create false {abs_}href properties, to trick Notify()
#
self
.
env
.
abs_href
=
Href
(
self
.
get_config
(
'project'
,
'url'
))
self
.
env
.
href
=
Href
(
self
.
get_config
(
'project'
,
'url'
))
tn
=
TicketNotifyEmail
(
self
.
env
)
if
self
.
notify_template
:
tn
.
template_name
=
self
.
notify_template
;
tn
.
notify
(
tkt
,
new
,
modtime
)
except
Exception
,
e
:
print
'TD: Failure sending notification on creation of ticket #
%
s:
%
s'
%
(
tkt
[
'id'
],
e
)
def
mail_line
(
self
,
str
):
return
'
%
s
%
s'
%
(
self
.
comment
,
str
)
def
html_mailto_link
(
self
,
subject
,
id
,
body
):
if
not
self
.
author
:
author
=
self
.
email_addr
else
:
author
=
self
.
author
# Must find a fix
#
#arr = string.split(body, '\n')
#arr = map(self.mail_line, arr)
#body = string.join(arr, '\n')
#body = '%s wrote:\n%s' %(author, body)
# Temporary fix
#
str
=
'mailto:
%
s?Subject=
%
s&Cc=
%
s'
%
(
urllib
.
quote
(
self
.
email_addr
),
urllib
.
quote
(
'Re: #
%
s:
%
s'
%
(
id
,
subject
)),
urllib
.
quote
(
self
.
MAILTO_CC
)
)
str
=
'
\r\n
{{{
\r\n
#!html
\r\n
<a href="
%
s">Reply to:
%
s</a>
\r\n
}}}
\r\n
'
%
(
str
,
author
)
return
str
def
attachments
(
self
,
message
,
ticket
,
update
=
False
):
'''
save any attachments as files in the ticket's directory
'''
count
=
0
first
=
0
number
=
0
for
part
in
message
.
walk
():
if
part
.
get_content_maintype
()
==
'multipart'
:
# multipart/* is just a container
continue
if
not
first
:
# first content is the message
first
=
1
if
part
.
get_content_type
()
==
'text/plain'
:
# if first is text, is was already put in the description
continue
filename
=
part
.
get_filename
()
count
=
count
+
1
if
not
filename
:
number
=
number
+
1
filename
=
'part
%04
d'
%
number
ext
=
mimetypes
.
guess_extension
(
part
.
get_content_type
())
if
not
ext
:
ext
=
'.bin'
filename
=
'
%
s
%
s'
%
(
filename
,
ext
)
else
:
filename
=
self
.
email_to_unicode
(
filename
)
# From the trac code
#
filename
=
filename
.
replace
(
'
\\
'
,
'/'
)
.
replace
(
':'
,
'/'
)
filename
=
os
.
path
.
basename
(
filename
)
# We try to normalize the filename to utf-8 NFC if we can.
# Files uploaded from OS X might be in NFD.
# Check python version and then try it
#
if
sys
.
version_info
[
0
]
>
2
or
(
sys
.
version_info
[
0
]
==
2
and
sys
.
version_info
[
1
]
>=
3
):
try
:
filename
=
unicodedata
.
normalize
(
'NFC'
,
unicode
(
filename
,
'utf-8'
))
.
encode
(
'utf-8'
)
except
TypeError
:
pass
url_filename
=
urllib
.
quote
(
filename
)
if
self
.
VERSION
==
0.8
:
dir
=
os
.
path
.
join
(
self
.
env
.
get_attachments_dir
(),
'ticket'
,
urllib
.
quote
(
str
(
ticket
[
'id'
])))
if
not
os
.
path
.
exists
(
dir
):
mkdir_p
(
dir
,
0755
)
else
:
dir
=
'/tmp'
path
,
fd
=
util
.
create_unique_file
(
os
.
path
.
join
(
dir
,
url_filename
))
text
=
part
.
get_payload
(
decode
=
1
)
if
not
text
:
text
=
'(None)'
fd
.
write
(
text
)
fd
.
close
()
# get the filesize
#
stats
=
os
.
lstat
(
path
)
filesize
=
stats
[
stat
.
ST_SIZE
]
# Insert the attachment it differs for the different TRAC versions
#
if
self
.
VERSION
==
0.8
:
cursor
=
self
.
db
.
cursor
()
try
:
cursor
.
execute
(
'INSERT INTO attachment VALUES("
%
s","
%
s","
%
s",
%
d,
%
d,"
%
s","
%
s","
%
s")'
%
(
'ticket'
,
urllib
.
quote
(
str
(
ticket
[
'id'
])),
filename
+
'?format=raw'
,
filesize
,
int
(
time
.
time
()),
''
,
self
.
author
,
'e-mail'
)
)
# Attachment is already known
#
except
sqlite
.
IntegrityError
:
#self.db.close()
return
count
self
.
db
.
commit
()
else
:
fd
=
open
(
path
)
att
=
attachment
.
Attachment
(
self
.
env
,
'ticket'
,
ticket
[
'id'
])
# This will break the ticket_update system, the body_text is vaporized
# ;-(
#
if
not
update
:
att
.
author
=
self
.
author
att
.
description
=
self
.
email_to_unicode
(
'Added by email2trac'
)
att
.
insert
(
url_filename
,
fd
,
filesize
)
fd
.
close
()
# Remove the created temporary filename
#
os
.
unlink
(
path
)
# Return how many attachments
#
return
count
def
mkdir_p
(
dir
,
mode
):
'''do a mkdir -p'''
arr
=
string
.
split
(
dir
,
'/'
)
path
=
''
for
part
in
arr
:
path
=
'
%
s/
%
s'
%
(
path
,
part
)
try
:
stats
=
os
.
stat
(
path
)
except
OSError
:
os
.
mkdir
(
path
,
mode
)
def
ReadConfig
(
file
,
name
):
"""
Parse the config file
"""
if
not
os
.
path
.
isfile
(
file
):
print
'File
%
s does not exist'
%
file
sys
.
exit
(
1
)
config
=
ConfigParser
.
ConfigParser
()
try
:
config
.
read
(
file
)
except
ConfigParser
.
MissingSectionHeaderError
,
detail
:
print
detail
sys
.
exit
(
1
)
# Use given project name else use defaults
#
if
name
:
if
not
config
.
has_section
(
name
):
print
"Not a valid project name:
%
s"
%
name
print
"Valid names:
%
s"
%
config
.
sections
()
sys
.
exit
(
1
)
project
=
dict
()
for
option
in
config
.
options
(
name
):
project
[
option
]
=
config
.
get
(
name
,
option
)
else
:
project
=
config
.
defaults
()
return
project
if
__name__
==
'__main__'
:
# Default config file
#
configfile
=
'/etc/email2trac.conf'
project
=
''
component
=
''
ENABLE_SYSLOG
=
0
try
:
opts
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
'chf:p:'
,
[
'component='
,
'help'
,
'file='
,
'project='
])
except
getopt
.
error
,
detail
:
print
__doc__
print
detail
sys
.
exit
(
1
)
project_name
=
None
for
opt
,
value
in
opts
:
if
opt
in
[
'-h'
,
'--help'
]:
print
__doc__
sys
.
exit
(
0
)
elif
opt
in
[
'-c'
,
'--component'
]:
component
=
value
elif
opt
in
[
'-f'
,
'--file'
]:
configfile
=
value
elif
opt
in
[
'-p'
,
'--project'
]:
project_name
=
value
settings
=
ReadConfig
(
configfile
,
project_name
)
if
not
settings
.
has_key
(
'project'
):
print
__doc__
print
'No Trac project is defined in the email2trac config file.'
sys
.
exit
(
1
)
if
component
:
settings
[
'component'
]
=
component
if
settings
.
has_key
(
'trac_version'
):
version
=
float
(
settings
[
'trac_version'
])
else
:
version
=
trac_default_version
if
settings
.
has_key
(
'enable_syslog'
):
ENABLE_SYSLOG
=
float
(
settings
[
'enable_syslog'
])
#debug HvB
#print settings
try
:
if
version
==
0.8
:
from
trac.Environment
import
Environment
from
trac.Ticket
import
Ticket
from
trac.Ticket
import
TicketNotifyEmail
from
trac.Href
import
Href
from
trac
import
util
import
sqlite
elif
version
==
0.9
:
from
trac
import
attachment
from
trac.env
import
Environment
from
trac.ticket
import
Ticket
from
trac.web.href
import
Href
from
trac
import
util
from
trac.Notify
import
TicketNotifyEmail
elif
version
==
0.10
:
from
trac
import
attachment
from
trac.env
import
Environment
from
trac.ticket
import
Ticket
from
trac.web.href
import
Href
from
trac
import
util
#
# return util.text.to_unicode(str)
#
# see http://projects.edgewall.com/trac/changeset/2799
from
trac.ticket.notification
import
TicketNotifyEmail
env
=
Environment
(
settings
[
'project'
],
create
=
0
)
tktparser
=
TicketEmailParser
(
env
,
settings
,
version
)
tktparser
.
parse
(
sys
.
stdin
)
# Catch all errors ans log to SYSLOG if we have enabled this
# else stdout
#
except
Exception
,
error
:
if
ENABLE_SYSLOG
:
syslog
.
openlog
(
'email2trac'
,
syslog
.
LOG_NOWAIT
)
etype
,
evalue
,
etb
=
sys
.
exc_info
()
for
e
in
traceback
.
format_exception
(
etype
,
evalue
,
etb
):
syslog
.
syslog
(
e
)
syslog
.
closelog
()
else
:
traceback
.
print_exc
()
if
m
:
tktparser
.
save_email_for_debug
(
m
,
True
)
# EOB
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment