Skip to content

Commit 36fb1c1

Browse files
committed
First complete version
1 parent 7b5863b commit 36fb1c1

File tree

6 files changed

+266
-3
lines changed

6 files changed

+266
-3
lines changed

README

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
1-
Simple python script that checks through imap and displays a notification.
1+
Simple python script that checks through imap and displays a notification. Currently tested only on ubuntu 11.04 Unity desktop.
22
The notification displays the sender, the subject and the first words of any text part the email may contain.
33
Also it changes the images if there are unseen emails.
4+
There have to be images called "normal.png" and "new.png" for the two status's in the same directory as mail.py. Be careful with the size, they should be around 16x16 pixels. (Sample images are provided)
45

56
It uses ezPyCrypto to decrypt the email and password using public/private keys. So as to not store the email and password in plain text.
7+
Download from http://www.freenet.org.nz/ezPyCrypto/ It explains also how to install
68

7-
There will be a script to generate your own private/public keys and another to create the email/password files with those keys.
9+
Setup
10+
Run "python create_keys.py" to generate the private and public keys.
11+
Run "python encrypt_info.py <email> <password>" to encode email and password.
12+
There should be 4 new files now:
13+
- .ex_mykey.pub ~> contains public key used to encrypt / generated by create_keys.py
14+
- .ex_mykey.priv ~> contains private key used to decrypt / generated by create_keys.py
15+
- email ~> binary file containing encrypted email address / generated by encrypt_info.py
16+
- pwd ~> binary file containing encrypted password / generated by encrypt_info.py
817

9-
Any questions, please ask.
18+
The last three files are vital for mail.py to work properly and must be in the same directory.
19+
20+
Now you can run the notifier 3 ways
21+
1.- python mail.py
22+
2.- ./mail.py
23+
3.- Double clicking the file and selecting run should work too, if the allow executing file as program is selected.
24+
25+
You can add the script to the
26+
27+
28+
Any questions, please ask.
29+
30+
WARNING: This script does not handle exceptions, if it stops working try running it from the console to see what the problem is.

create_keys.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
create_keys.py
5+
6+
Create and save public and private keys
7+
"""
8+
9+
import os
10+
import ezPyCrypto
11+
12+
directory=os.path.dirname(os.path.realpath(__file__))
13+
# Create a key object
14+
k = ezPyCrypto.key(1280)
15+
16+
# Export private and public/private keys
17+
publicKey = k.exportKey()
18+
publicAndPrivateKey = k.exportKeyPrivate()
19+
20+
# Save these to disk (. to hide them in linux at least)
21+
fd = open(directory+"/.ex_mykey.pub", "w")
22+
fd.write(publicKey)
23+
fd.close()
24+
25+
fd = open(directory+"/.ex_mykey.priv", "w")
26+
fd.write(publicAndPrivateKey)
27+
fd.close()

encrypt_info.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python
2+
3+
"""
4+
encrypt_info.py
5+
6+
Import a public key, and encrypt email and password
7+
"""
8+
9+
import os
10+
import sys
11+
import ezPyCrypto
12+
13+
def usage():
14+
print "Usage:"
15+
print "encrypt_info.py [email protected] password"
16+
17+
if len(sys.argv) != 3:
18+
usage()
19+
exit()
20+
21+
email= sys.argv[1]
22+
pwd= sys.argv[2]
23+
print "Email: " + email
24+
print "Password: " + pwd
25+
print "Remember to escape weird characters like $ with a \ this way ~> \$"
26+
27+
directory=os.path.dirname(os.path.realpath(__file__))
28+
29+
# Create a key object
30+
k = ezPyCrypto.key(1280)
31+
32+
# Read in the public key
33+
fd = open(directory+"/.ex_mykey.pub", "rb")
34+
print "Reading public key: .ex_mykey.pub"
35+
pubkey = fd.read()
36+
fd.close()
37+
38+
# import this public key
39+
k.importKey(pubkey)
40+
41+
# Now encrypt the email against this public key
42+
print "Encrypting email..."
43+
enc = k.encString(email)
44+
45+
# Save the encrypted email to disk
46+
print "Saving encrypted email to: email..."
47+
fd = open(directory+"/email", "wb")
48+
fd.write(enc)
49+
fd.close()
50+
51+
# Now encrypt the password against this public key
52+
print "encrypting password..."
53+
enc = k.encString(pwd)
54+
55+
# Save the encrypted password to disk
56+
print "Saving encrypted password to: pwd..."
57+
fd = open(directory+"/pwd", "wb")
58+
fd.write(enc)
59+
fd.close()
60+
print "Success"

mail.py

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import gtk
5+
import appindicator
6+
import pynotify
7+
8+
import imaplib
9+
import re
10+
import email.header
11+
import ezPyCrypto
12+
import quopri
13+
import os
14+
15+
PING_FREQUENCY = 10 # seconds
16+
17+
class CheckGMail:
18+
def __init__(self):
19+
directory= os.path.dirname(os.path.realpath(__file__))
20+
self.ind = appindicator.Indicator("new-gmail-indicator", directory+"/normal.png" , appindicator.CATEGORY_APPLICATION_STATUS)
21+
self.ind.set_status(appindicator.STATUS_ACTIVE)
22+
self.ind.set_attention_icon(directory+"/new.png")
23+
24+
self.menu_setup()
25+
self.ind.set_menu(self.menu)
26+
self.last_count=0
27+
28+
fd = open(directory+"/.ex_mykey.priv","rb")
29+
k = ezPyCrypto.key(fd.read())
30+
fd.close()
31+
32+
fd=open(directory+"/email", "rb")
33+
self.email= k.decString(fd.read())
34+
fd.close()
35+
fd=open(directory+"/pwd", "rb")
36+
self.pwd= k.decString(fd.read())
37+
fd.close()
38+
39+
def menu_setup(self):
40+
self.menu = gtk.Menu()
41+
42+
self.quit_item = gtk.MenuItem("Quit")
43+
self.quit_item.connect("activate", self.quit)
44+
self.quit_item.show()
45+
self.menu.append(self.quit_item)
46+
47+
def main(self):
48+
self.check_mail()
49+
gtk.timeout_add(PING_FREQUENCY * 1000, self.check_mail)
50+
gtk.main()
51+
52+
def quit(self, widget):
53+
sys.exit(0)
54+
55+
def unir(self, arreglo):
56+
res=[]
57+
for i in arreglo:
58+
if i[1] != None:
59+
res.append(i[0].decode(i[1]))
60+
else:
61+
res.append(i[0])
62+
return ''.join(res)
63+
64+
def get_first_text_part(self, msg):
65+
maintype = msg.get_content_maintype()
66+
if maintype == 'multipart':
67+
for part in msg.get_payload():
68+
print part.get_content_charset(part.get_payload())
69+
if part.get_content_maintype() == 'text':
70+
resp= ' '
71+
if part['Content-Transfer-Encoding'] == 'quoted-printable':
72+
resp= quopri.decodestring(part.get_payload())
73+
if part.get_content_charset(False):
74+
resp = part.get_payload().decode(part.get_content_charset())
75+
print resp
76+
return resp
77+
for part in msg.get_payload():
78+
return self.get_first_text_part(part)
79+
elif maintype == 'text':
80+
resp= ''
81+
print msg.get_content_charset(msg.get_payload())
82+
if msg['Content-Transfer-Encoding'] == 'quoted-printable':
83+
resp= quopri.decodestring(msg.get_payload())
84+
if msg.get_content_charset(False):
85+
resp = msg.get_payload().decode(msg.get_content_charset())
86+
print resp
87+
return resp
88+
else:
89+
return ' '
90+
91+
def check_mail(self):
92+
messages, unread, msg_notify = self.gmail_checker(self.email, self.pwd)
93+
print ''.join(msg_notify)
94+
if unread > 0:
95+
self.ind.set_status(appindicator.STATUS_ATTENTION)
96+
if unread > self.last_count:
97+
n = pynotify.Notification(str(unread)+" new e-mail(s)", ''.join(msg_notify), "notification")
98+
n.show()
99+
else:
100+
self.ind.set_status(appindicator.STATUS_ACTIVE)
101+
self.last_count= unread
102+
return True
103+
104+
def gmail_checker(self, username, password):
105+
i = imaplib.IMAP4_SSL('imap.gmail.com')
106+
i.login(username, password)
107+
x, y = i.status('INBOX', '(MESSAGES UNSEEN)')
108+
i.select('INBOX','readonly')
109+
typ, data = i.search(None,'UNSEEN')
110+
msg_notify= []
111+
messages=0
112+
unseen=0
113+
if typ == 'OK':
114+
for msg_num in data[0].split():
115+
typ, msg = i.fetch(str(msg_num), "RFC822")
116+
if typ == 'OK':
117+
mail_msg = email.message_from_string(msg[0][1])
118+
aux= email.header.decode_header(mail_msg['from'])
119+
fr=self.unir(aux)
120+
if len(fr) > 40:
121+
fr= fr[:37]+'...'
122+
try:
123+
msg_notify.append('From: '+ fr.encode('utf-8') +'\n')
124+
except:
125+
msg_notify.append('From: ##Encode error##\n')
126+
aux=email.header.decode_header(mail_msg['subject'])
127+
sub=self.unir(aux)
128+
if len(sub) > 40:
129+
sub= sub[:37]+'...'
130+
try:
131+
msg_notify.append('Subject: ' + sub.encode('utf-8') +'\n')
132+
except:
133+
msg_notify.append('From: ##Encode error##\n')
134+
bod=self.get_first_text_part(mail_msg)
135+
print bod.__class__
136+
if not isinstance(bod, str) and not type(bod).__name__ == 'unicode':
137+
bod='(No content)'
138+
bod=re.sub('[ \t\n\r]+',' ', bod)
139+
if re.search("<html>", bod.lower()):
140+
bod=re.sub('<.*?>', '', bod)
141+
if len(bod) > 40:
142+
bod=(bod[:37]+'...')
143+
#print bod
144+
try:
145+
msg_notify.append('Msg: '+ bod.encode('utf-8') + '\n')
146+
except:
147+
msg_notify.append('Msg: ##Encode error##\n')
148+
messages = int(re.search('MESSAGES\s+(\d+)', y[0]).group(1))
149+
unseen = int(re.search('UNSEEN\s+(\d+)', y[0]).group(1))
150+
return (messages, unseen, msg_notify)
151+
152+
if __name__ == "__main__":
153+
indicator = CheckGMail()
154+
indicator.main()
155+

new.png

685 Bytes
Loading

normal.png

588 Bytes
Loading

0 commit comments

Comments
 (0)