├── README.md ├── form.py ├── ga-cli.py └── gui.py /README.md: -------------------------------------------------------------------------------- 1 | # Gmail Attachment Downloader 2 | 3 | This program is a Python script that downloads attachments found in the inbox of the user's Gmail account, based on a specific sender's address. 4 | 5 | ## Getting Started 6 | 7 | ### Prerequisites 8 | 9 | - Python 2.5+ 10 | 11 | ### Installing 12 | 13 | 1. Enable IMAP after logging in to your Google (Gmail) account. 14 | 2. Give permission to [less secure apps in Gmail](https://myaccount.google.com/lesssecureapps). 15 | 3. Download/clone repository. 16 | 4. Run `python gui.py` 17 | 18 | [Note that a CLI-specific version has also been added; and can be used by : `python ga-cli.py`] 19 | 20 | ## Usage 21 | 22 | - Interface presents you with Your e-mail, Password and Query e-mail fields. 23 | - After correctly entering all the fields, hit Enter or click OK. 24 | - Finally, it will download all attachments from the queried sender's emails onto the specific directory. 25 | - You can view each email's contents from the list displayed. 26 | - It will not download an attachment if it already exists in the directory. 27 | 28 | ## Built With 29 | 30 | - Python 31 | - QT 4 32 | - The following packages from the Python 3 standard libraries: 33 | - [imaplib](https://docs.python.org/3/library/imaplib.html) 34 | - [email](https://docs.python.org/3/library/email.html) 35 | 36 | -------------------------------------------------------------------------------- /form.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'created.ui' 4 | # 5 | # Created by: PyQt4 UI code generator 4.11.4 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt4 import QtCore, QtGui 10 | 11 | try: 12 | _fromUtf8 = QtCore.QString.fromUtf8 13 | except AttributeError: 14 | def _fromUtf8(s): 15 | return s 16 | 17 | try: 18 | _encoding = QtGui.QApplication.UnicodeUTF8 19 | def _translate(context, text, disambig): 20 | return QtGui.QApplication.translate(context, text, disambig, _encoding) 21 | except AttributeError: 22 | def _translate(context, text, disambig): 23 | return QtGui.QApplication.translate(context, text, disambig) 24 | 25 | class Ui_Dialog(object): 26 | def setupUi(self, Dialog): 27 | Dialog.setObjectName(_fromUtf8("Dialog")) 28 | Dialog.resize(424, 507) 29 | sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) 30 | sizePolicy.setHorizontalStretch(0) 31 | sizePolicy.setVerticalStretch(0) 32 | sizePolicy.setHeightForWidth(Dialog.sizePolicy().hasHeightForWidth()) 33 | Dialog.setSizePolicy(sizePolicy) 34 | self.label = QtGui.QLabel(Dialog) 35 | self.label.setGeometry(QtCore.QRect(50, 49, 81, 31)) 36 | self.label.setObjectName(_fromUtf8("label")) 37 | self.label_2 = QtGui.QLabel(Dialog) 38 | self.label_2.setGeometry(QtCore.QRect(60, 80, 68, 31)) 39 | self.label_2.setObjectName(_fromUtf8("label_2")) 40 | self.label_3 = QtGui.QLabel(Dialog) 41 | self.label_3.setGeometry(QtCore.QRect(40, 140, 91, 31)) 42 | self.label_3.setObjectName(_fromUtf8("label_3")) 43 | self.listWidget = QtGui.QListWidget(Dialog) 44 | self.listWidget.setGeometry(QtCore.QRect(25, 220, 391, 271)) 45 | self.listWidget.setProperty("sdfsd", _fromUtf8("")) 46 | self.listWidget.setObjectName(_fromUtf8("listWidget")) 47 | self.lineEdit = QtGui.QLineEdit(Dialog) 48 | self.lineEdit.setGeometry(QtCore.QRect(150, 50, 261, 27)) 49 | self.lineEdit.setObjectName(_fromUtf8("lineEdit")) 50 | self.lineEdit_2 = QtGui.QLineEdit(Dialog) 51 | self.lineEdit_2.setGeometry(QtCore.QRect(150, 80, 261, 27)) 52 | self.lineEdit_2.setEchoMode(QtGui.QLineEdit.Password) 53 | self.lineEdit_2.setObjectName(_fromUtf8("lineEdit_2")) 54 | self.lineEdit_3 = QtGui.QLineEdit(Dialog) 55 | self.lineEdit_3.setGeometry(QtCore.QRect(150, 140, 261, 27)) 56 | self.lineEdit_3.setObjectName(_fromUtf8("lineEdit_3")) 57 | self.pushButton = QtGui.QPushButton(Dialog) 58 | self.pushButton.setGeometry(QtCore.QRect(200, 180, 99, 27)) 59 | self.pushButton.setObjectName(_fromUtf8("pushButton")) 60 | 61 | self.retranslateUi(Dialog) 62 | QtCore.QMetaObject.connectSlotsByName(Dialog) 63 | 64 | def retranslateUi(self, Dialog): 65 | Dialog.setWindowTitle(_translate("Dialog", "Gmail A F", None)) 66 | self.label.setText(_translate("Dialog", "Your e-mail", None)) 67 | self.label_2.setText(_translate("Dialog", "Password", None)) 68 | self.label_3.setText(_translate("Dialog", "Query e-mail", None)) 69 | self.pushButton.setText(_translate("Dialog", "OK", None)) 70 | 71 | -------------------------------------------------------------------------------- /ga-cli.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import imaplib 3 | import getpass 4 | import email 5 | import email.header 6 | import datetime 7 | import os 8 | 9 | reload(sys) 10 | sys.setdefaultencoding('utf8') 11 | 12 | curr_dir ='.' 13 | 14 | EMAIL_ACCOUNT= raw_input("Enter your email: ") 15 | #EMAIL_ACCOUNT = "spotifyrounak@gmail.com" 16 | EMAIL_FOLDER = "INBOX" # or "[Gmail]/All Mail" or "[Gmail]/Sent Mail" 17 | 18 | #search_email = "rounakdatta12@gmail.com" 19 | 20 | def process_mailbox(M): 21 | 22 | rv, data = M.search(None, '(OR (TO %s) (FROM %s))' % (search_email, search_email)) 23 | if rv != 'OK': 24 | print "No messages found!" 25 | return 26 | 27 | for num in data[0].split(): 28 | rv, data = M.fetch(num, '(RFC822)') 29 | if rv != 'OK': 30 | print "ERROR getting message", num 31 | return 32 | 33 | print '-----' 34 | msg = email.message_from_string(data[0][1]) 35 | decode = email.header.decode_header(msg['Subject'])[0] 36 | subject = unicode(decode[0]) 37 | print 'Message %s: %s' % (num, subject) 38 | 39 | sender = msg['from'].split()[-1] 40 | sender = sender[1:] 41 | sender = sender[:-1] 42 | 43 | print 'Sender: ', sender 44 | #print 'Raw Date:', msg['Date'] 45 | 46 | for part in msg.walk(): 47 | if part.get_content_type() == 'text/plain': 48 | print part.get_payload() 49 | 50 | # Now convert to local date-time 51 | date_tuple = email.utils.parsedate_tz(msg['Date']) 52 | if date_tuple: 53 | local_date = datetime.datetime.fromtimestamp( 54 | email.utils.mktime_tz(date_tuple)) 55 | print "Dated: ", \ 56 | local_date.strftime("%a, %d %b %Y %H:%M:%S") 57 | 58 | fcount = 0 59 | for part in msg.walk(): 60 | 61 | if(part.get('Content-Disposition' ) is not None ) : 62 | filename = part.get_filename() 63 | print filename 64 | 65 | 66 | 67 | final_path= os.path.join(curr_dir + filename) 68 | 69 | if not os.path.isfile(final_path) : 70 | 71 | fp = open(curr_dir+"/"+(filename), 'wb') 72 | fp.write(part.get_payload(decode=True)) 73 | fcount += 1 74 | fp.close() 75 | 76 | print '%d attachment(s) fetched' %fcount 77 | print '-----\n\n' 78 | 79 | 80 | M = imaplib.IMAP4_SSL('imap.gmail.com') 81 | 82 | try: 83 | rv, data = M.login(EMAIL_ACCOUNT, getpass.getpass()) 84 | except imaplib.IMAP4.error: 85 | print "LOGIN FAILED!!! " 86 | sys.exit(1) 87 | 88 | print data 89 | search_email = raw_input("Enter search email: ") 90 | if search_email not in os.listdir(curr_dir): 91 | os.mkdir(search_email) 92 | 93 | curr_dir = './'+search_email 94 | 95 | rv, mailboxes = M.list() 96 | if rv == 'OK': 97 | print "Mailboxes located." 98 | #print mailboxes 99 | 100 | rv, data = M.select(EMAIL_FOLDER) 101 | 102 | if rv == 'OK': 103 | print "Now processing mailbox ...\n" 104 | process_mailbox(M) 105 | M.close() 106 | else: 107 | print "ERROR: Unable to open mailbox ", rv 108 | 109 | M.logout() -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import imaplib 3 | import getpass 4 | import email 5 | import email.header 6 | import datetime 7 | import os 8 | from PyQt4 import QtCore, QtGui 9 | from PyQt4.QtGui import * 10 | from form import Ui_Dialog 11 | 12 | reload(sys) 13 | sys.setdefaultencoding('utf8') 14 | 15 | curr_dir ='.' 16 | 17 | EMAIL_ACCOUNT= None 18 | 19 | EMAIL_FOLDER = "INBOX" # or "[Gmail]/All Mail" or "[Gmail]/Sent Mail" 20 | 21 | mailinfo = [] 22 | 23 | class MyDialog(QtGui.QDialog): 24 | def __init__(self, parent=None): 25 | QtGui.QWidget.__init__(self, parent) 26 | self.ui = Ui_Dialog() 27 | self.ui.setupUi(self) 28 | self.ui.pushButton.clicked.connect(self.OK) 29 | self.ui.listWidget.itemClicked.connect(self.showmessage) 30 | 31 | def showmessage(self, item): 32 | msgnumber = str(item.text()).split()[1] 33 | msgindex = mailinfo.index(msgnumber) 34 | print mailinfo[msgindex + 1] 35 | print mailinfo[msgindex + 2] 36 | print mailinfo[msgindex + 3] 37 | print mailinfo[msgindex + 4] 38 | 39 | boxy = QMessageBox() 40 | boxy.setText('Title : %s\nFrom : %s' %((mailinfo[msgindex + 1]), (mailinfo[msgindex + 2]))) 41 | boxy.setInformativeText('%s\n\n%s attachment(s) on this message' % ((mailinfo[msgindex + 3]), (mailinfo[msgindex + 4]))) 42 | boxy.setWindowTitle('Message %s' % (mailinfo[msgindex])) 43 | 44 | boxy.setStandardButtons(QMessageBox.Ok) 45 | boxy.exec_() 46 | 47 | 48 | def OK(self): 49 | EMAIL_ACCOUNT = self.ui.lineEdit.text() 50 | 51 | print 'OK pressed.' 52 | self.auth(EMAIL_ACCOUNT) 53 | 54 | def auth(self, EMAIL_ACCOUNT): 55 | M = imaplib.IMAP4_SSL('imap.gmail.com') 56 | try: 57 | rv, data = M.login(EMAIL_ACCOUNT, self.ui.lineEdit_2.text()) 58 | except imaplib.IMAP4.error: 59 | choice=QtGui.QMessageBox.question(self, 'Error', "LOGIN FAILED!!!", QtGui.QMessageBox.Retry, QtGui.QMessageBox.Exit) 60 | if choice=QtGui.QMessageBox.Exit: 61 | #print "LOGIN FAILED!!! " 62 | sys.exit(1) 63 | elif choice=QtGui.QMessageBox.Retry: 64 | auth(self, EMAIL_ACCOUNT) 65 | 66 | print data 67 | search_email = self.ui.lineEdit_3.text() 68 | 69 | curr_dir = '.' 70 | 71 | if search_email not in os.listdir(curr_dir): 72 | os.mkdir(search_email) 73 | 74 | curr_dir = './'+search_email 75 | 76 | rv, mailboxes = M.list() 77 | if rv == 'OK': 78 | print "Mailboxes located." 79 | 80 | rv, data = M.select(EMAIL_FOLDER) 81 | 82 | if rv == 'OK': 83 | print "Now processing mailbox ...\n" 84 | self.process_mailbox(M, search_email, curr_dir) 85 | M.close() 86 | else: 87 | print "ERROR: Unable to open mailbox ", rv 88 | 89 | M.logout() 90 | 91 | def process_mailbox(self, M, search_email, curr_dir): 92 | 93 | rv, data = M.search(None, '(OR (TO %s) (FROM %s))' % (search_email, search_email)) 94 | if rv != 'OK': 95 | print "No messages found!" 96 | return 97 | 98 | for num in data[0].split(): 99 | rv, data = M.fetch(num, '(RFC822)') 100 | if rv != 'OK': 101 | print "ERROR getting message", num 102 | return 103 | 104 | localinfo = [] 105 | 106 | print '-----' 107 | msg = email.message_from_string(data[0][1]) 108 | decode = email.header.decode_header(msg['Subject'])[0] 109 | subject = unicode(decode[0]) 110 | print 'Message %s : %s' % (num, subject) 111 | self.ui.listWidget.addItem(QListWidgetItem('Message %s : %s' % (num, subject))) 112 | localinfo.append(num) 113 | localinfo.append(subject) 114 | 115 | sender = msg['from'].split()[-1] 116 | sender = sender[1:] 117 | sender = sender[:-1] 118 | 119 | print 'Sender : ', sender 120 | localinfo.append(sender) 121 | #print 'Raw Date:', msg['Date'] 122 | 123 | for part in msg.walk(): 124 | if part.get_content_type() == 'text/plain': 125 | print part.get_payload() 126 | localinfo.append(part.get_payload()) 127 | 128 | # Kepping up to local time 129 | date_tuple = email.utils.parsedate_tz(msg['Date']) 130 | if date_tuple: 131 | local_date = datetime.datetime.fromtimestamp( 132 | email.utils.mktime_tz(date_tuple)) 133 | print "Dated : ", \ 134 | local_date.strftime("%a, %d %b %Y %H:%M:%S") 135 | 136 | 137 | fcount = 0 138 | for part in msg.walk(): 139 | 140 | if(part.get('Content-Disposition' ) is not None ) : 141 | filename = part.get_filename() 142 | print filename 143 | 144 | 145 | 146 | final_path= os.path.join(curr_dir + filename) 147 | 148 | if not os.path.isfile(final_path) : 149 | 150 | fp = open(curr_dir+"/"+(filename), 'wb') 151 | fp.write(part.get_payload(decode=True)) 152 | fcount += 1 153 | fp.close() 154 | 155 | localinfo.append(fcount) 156 | 157 | global mailinfo 158 | mailinfo.append(localinfo) 159 | 160 | print '%d attachment(s) fetched' %fcount 161 | print '-----\n\n' 162 | 163 | mailinfo = [item for sublist in mailinfo for item in sublist] 164 | print mailinfo 165 | 166 | if __name__ == "__main__": 167 | app = QtGui.QApplication(sys.argv) 168 | myapp = MyDialog() 169 | myapp.show() 170 | sys.exit(app.exec_()) 171 | 172 | --------------------------------------------------------------------------------