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
Franco (nextime) Lanza
email2trac
Commits
3f38d993
Commit
3f38d993
authored
May 01, 2015
by
nextime
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
first commit
parents
Changes
2
Show 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
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
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