def ok(event=None): with open(os.path.join(LOCAL_PATH, '.pwd')) as fich: cryptedpwd = fich.read() old = oldpwd.get() pwd = newpwd.get() pwd2 = confpwd.get() if crypt.crypt(old, cryptedpwd) == cryptedpwd: # passwords match if pwd == pwd2: # new passwords match cryptedpwd = crypt.crypt(pwd, crypt.mksalt(crypt.METHOD_SHA512)) with open(os.path.join(LOCAL_PATH, '.pwd'), "w") as fich: fich.write(cryptedpwd) mailboxes = CONFIG.get( "Mailboxes", "active").split(", ") + CONFIG.get( "Mailboxes", "inactive").split(", ") while "" in mailboxes: mailboxes.remove("") for mailbox in mailboxes: server, login, password, folder = decrypt(mailbox, old) if server is not None: encrypt(mailbox, pwd, server, login, password, folder) self.pwd = pwd top.destroy() logging.info('Password changed') return pwd else: showerror(_('Error'), _('Passwords do not match!')) else: showerror(_('Error'), _('Incorrect password!'))
def save(self, event=None): self.name = name = self.name_entry.get().strip() if name != self.mailbox: # change name of mailbox os.remove(os.path.join(LOCAL_PATH, self.mailbox)) active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") if self.mailbox in active: active.remove(self.mailbox) active.append(name) elif self.mailbox in inactive: inactive.remove(self.mailbox) inactive.append(name) CONFIG.set("Mailboxes", "active", ", ".join(active)) CONFIG.set("Mailboxes", "inactive", ", ".join(inactive)) save_config() encrypt(name, self.pwd, self.server_entry.get().strip(), self.login_entry.get().strip(), self.password_entry.get().strip(), self.folder_entry.get().strip()) self.destroy()
def __init__(self, master, pwd): """Create the mailbox manager dialog.""" Toplevel.__init__(self, master, class_="CheckMails") self.title(_("Mailbox Manager")) self.minsize(200, 10) self.pwd = pwd self.resizable(False, False) self.protocol("WM_DELETE_WINDOW", self.quit) self.im_add = PhotoImage(master=self, file=ADD) self.im_del = PhotoImage(master=self, file=DEL) self.im_edit = PhotoImage(master=self, file=EDIT) self.mailboxes = {} active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") active.sort() inactive.sort() self.frame = Frame(self) self.columnconfigure(0, weight=1) self.frame.columnconfigure(1, weight=1) self.frame.grid(row=0, column=0, padx=10, pady=10, sticky="eswn") i = -1 for i, box in enumerate(active): c = Checkbutton(self.frame) c.state(('selected',)) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] for box in inactive: i += 1 c = Checkbutton(self.frame) c.grid(row=i, column=0, pady=4, padx=(4, 0)) l = Label(self.frame, text=box) l.grid(row=i, column=1, padx=4, pady=4) b_edit = Button(self.frame, image=self.im_edit, width=1, command=lambda m=box: self.mailbox_info(m)) b_edit.grid(row=i, column=2, padx=4, pady=4) b_del = Button(self.frame, image=self.im_del, width=1, command=lambda m=box: self.del_mailbox(m)) b_del.grid(row=i, column=3, padx=4, pady=4) self.mailboxes[box] = [c, l, b_edit, b_del] self.b_add = Button(self.frame, image=self.im_add, command=self.mailbox_info, width=1) self.b_add.grid(row=i + 1, column=0, columnspan=4, pady=4, padx=4, sticky='w')
def launch_check(self, force_notify=False): """ Check every 20 s if the login to all the mailboxes is done. Once it is the case, launch the unread mail check. """ b = [ self.threads_connect[box].is_alive() for box in self.threads_connect ] if len(b) < len(self.info_conn) or True in b: logging.info("Waiting for connexion ...") try: self.after_cancel(self.check_id) except ValueError: pass self.check_id = self.after(20000, self.launch_check, force_notify) else: logging.info("Launching check") if not self.login_err_queue.empty(): correct = False while not self.login_err_queue.empty(): box = self.login_err_queue.get() action = show_failed_auth_msg(self, box) if action == 'correct': dialog = EditMailbox(self, self.pwd, box) self.wait_window(dialog) self.connect(box) correct = dialog.name or correct else: # remove box from the active mailboxes del (self.boxes[box]) active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") active.remove(box) inactive.append(box) CONFIG.set("Mailboxes", "active", ", ".join(active)) CONFIG.set("Mailboxes", "inactive", ", ".join(inactive)) if correct: self.after_cancel(self.check_id) self.check_id = self.after(20000, self.launch_check, force_notify) else: self.check_mails(force_notify) else: self.check_mails(force_notify)
def reset_password(self): """ Reset the master password and delete all the mailboxes config files since they cannot be decrypted without the password. """ rep = askokcancel( _("Confirmation"), _("The reset of the password will erase all the stored mailbox connection information" ), icon="warning") if rep: mailboxes = CONFIG.get("Mailboxes", "active").split(", ") + CONFIG.get( "Mailboxes", "inactive").split(", ") while "" in mailboxes: mailboxes.remove("") CONFIG.set("Mailboxes", "active", "") CONFIG.set("Mailboxes", "inactive", "") save_config() for mailbox in mailboxes: os.remove(os.path.join(LOCAL_PATH, mailbox)) logging.info('Reset') self.set_password()
def change_icon(self, nbmail): """Display the number of unread mails nbmail in the system tray icon.""" nb = "%i" % nbmail im = Image.open(IMAGE) W, H = im.size draw = ImageDraw.Draw(im) font_path = TTF_FONTS[CONFIG.get("General", "font")] try: font = ImageFont.truetype(font_path, FONTSIZE) w, h = draw.textsize(nb, font=font) draw.text(((W - w) / 2, (H - h) / 2), nb, fill=(255, 0, 0), font=font) except OSError: w, h = draw.textsize(nb) draw.text(((W - w) / 2, (H - h) / 2), nb, fill=(255, 0, 0)) im.save(ICON) self.icon.change_icon(ICON, "checkmails %s" % nb)
def get_info_conn(self): """ Retrieve connection information from the encrypted files and launch checks (if they are noit suspended). """ mailboxes = CONFIG.get("Mailboxes", "active").split(", ") while "" in mailboxes: mailboxes.remove("") self.info_conn = {} if self.pwd is None: if not os.path.exists(os.path.join(LOCAL_PATH, ".pwd")): self.set_password() else: self.ask_password() if self.pwd is not None: for box in mailboxes: server, login, password, folder = decrypt(box, self.pwd) if server is not None: self.info_conn[box] = (server, (login, password), folder) if not self.info_conn: self.notif = _("No active mailbox") run([ "notify-send", "-i", IMAGE2, _("No active mailbox"), _("Use the mailbox manager to configure a mailbox.") ]) elif self.icon.get_item_label(3) == _("Suspend"): self.notif = "" for box in self.info_conn: self.connect(box) try: self.after_cancel(self.check_id) except ValueError: pass self.check_id = self.after(20000, self.launch_check, False)
def __init__(self, master): Toplevel.__init__(self, master, class_="CheckMails") self.title(_("Preferences")) style = Style(self) style.map("TCombobox", fieldbackground=[('readonly', 'white')], selectbackground=[('readonly', 'white')], selectforeground=[('readonly', 'black')], foreground=[('readonly', 'black')]) # validation of the entries : only numbers are allowed self._validate_entry_nb = self.register(self.validate_entry_nb) # --- Times Label(self, text=_("Time between two checks")).grid(row=0, column=0, padx=(10, 4), pady=(10, 4), sticky="e") Label(self, justify="right", text=_("Maximum time allowed for login or check\n\ (then the connection is reset)")).grid(row=1, column=0, padx=(10, 4), pady=4, sticky="e") self.time_entry = Entry(self, width=5, justify="center", validate="key", validatecommand=(self._validate_entry_nb, "%P")) self.time_entry.grid(row=0, column=1, padx=(4, 0), pady=(10, 4)) self.time_entry.insert(0, "%g" % (CONFIG.getint("General", "time") / 60000)) self.timeout_entry = Entry(self, width=5, justify="center", validate="key", validatecommand=(self._validate_entry_nb, "%P")) self.timeout_entry.grid(row=1, column=1, padx=(4, 0), pady=4) self.timeout_entry.insert(0, "%g" % (CONFIG.getint("General", "timeout") / 60000)) Label(self, text="min").grid(row=0, column=2, padx=(0, 10), pady=(10, 4)) Label(self, text="min").grid(row=1, column=2, padx=(0, 10), pady=4) frame = Frame(self) frame.grid(row=2, columnspan=3, padx=6, pady=(0, 6)) # --- Language Label(frame, text=_("Language")).grid(row=0, column=0, padx=8, pady=4, sticky="e") lang = {"fr": "Français", "en": "English"} self.lang = StringVar(self, lang[CONFIG.get("General", "language")]) menu_lang = Menu(frame, tearoff=False) Menubutton(frame, menu=menu_lang, width=9, textvariable=self.lang).grid(row=0, column=1, padx=8, pady=4, sticky="w") menu_lang.add_radiobutton(label="English", value="English", variable=self.lang, command=self.translate) menu_lang.add_radiobutton(label="Français", value="Français", variable=self.lang, command=self.translate) # --- gui toolkit Label(frame, text=_("GUI Toolkit for the system tray icon")).grid(row=1, column=0, padx=8, pady=4, sticky="e") self.gui = StringVar(self, CONFIG.get("General", "trayicon").capitalize()) menu_gui = Menu(frame, tearoff=False) Menubutton(frame, menu=menu_gui, width=9, textvariable=self.gui).grid(row=1, column=1, padx=8, pady=4, sticky="w") for toolkit, b in TOOLKITS.items(): if b: menu_gui.add_radiobutton(label=toolkit.capitalize(), value=toolkit.capitalize(), variable=self.gui, command=self.change_gui) # --- Font self.preview_path = tempfile.mktemp(".png", "checkmails_preview") w = max([len(f) for f in TTF_FONTS]) self.fonts = sorted(TTF_FONTS) self.font = Combobox(frame, values=self.fonts, width=(w * 2) // 3, exportselection=False, state="readonly") current_font = CONFIG.get("General", "font") if current_font in self.fonts: i = self.fonts.index(current_font) else: i = 0 self.font.current(i) self.img_prev = PhotoImage(master=self, file=IMAGE) Label(frame, text=_("Font")).grid(row=2, column=0, padx=8, pady=4, sticky="e") self.font.grid(row=2, column=1, padx=8, pady=4, sticky="w") self.prev = Label(frame, image=self.img_prev) self.prev.grid(row=2, column=2, padx=8, pady=4) self.update_preview() self.font.bind('<<ComboboxSelected>>', self.update_preview) self.font.bind_class("ComboboxListbox", '<KeyPress>', self.key_nav) # --- Ok/Cancel frame_button = Frame(self) frame_button.grid(row=3, columnspan=3, padx=6, pady=(0, 6)) Button(frame_button, text="Ok", command=self.ok).grid(row=2, column=0, padx=8, pady=4) Button(frame_button, text=_("Cancel"), command=self.destroy).grid(row=2, column=1, padx=4, pady=4)
def connect_mailbox(self, box): """Connect to the mailbox box and select the folder.""" try: logging.info("Connecting to %s" % box) serveur, loginfo, folder = self.info_conn[box] # reinitialize the connection if it takes too long timeout_id = self.after(self.timeout, self.timed_out, box, False, True) self.boxes[box] = IMAP4_SSL(serveur) self.boxes[box].login(*loginfo) self.boxes[box].select(folder) try: self.after_cancel(timeout_id) except ValueError: pass logging.info("Connected to %s" % box) except (IMAP4.error, ConnectionResetError, TimeoutError) as e: try: self.after_cancel(timeout_id) except ValueError: pass if e.args[0] in [ b'Invalid login or password', b'Authenticate error', b'Login failed: authentication failure', b'LOGIN failed' ]: # Identification error logging.error("Incorrect login or password for %(mailbox)s" % {"mailbox": box}) self.login_err_queue.put(box) else: # try to reconnect logging.error('%s: %s' % (box, e)) self.logout(box, reconnect=True) except gaierror as e: if e.errno == -2: # Either there is No Internet connection or the IMAP server is wrong if internet_on(): run([ "notify-send", "-i", "dialog-error", _("Error"), _("Wrong IMAP server for %(mailbox)s.") % { "mailbox": box } ]) # remove box from the active mailboxes active = CONFIG.get("Mailboxes", "active").split(", ") inactive = CONFIG.get("Mailboxes", "inactive").split(", ") while "" in active: active.remove("") while "" in inactive: inactive.remove("") active.remove(box) inactive.append(box) CONFIG.set("Mailboxes", "active", ", ".join(active)) CONFIG.set("Mailboxes", "inactive", ", ".join(inactive)) logging.error("Wrong IMAP server for %(mailbox)s." % {"mailbox": box}) else: if self.notify_no_internet: run([ "notify-send", "-i", "dialog-error", _("Error"), _("No Internet connection.") ]) self.notify_no_internet = False logging.warning("No Internet connection") # cancel everything try: self.after_cancel(self.check_id) except ValueError: pass try: self.after_cancel(self.timer_id) except ValueError: pass try: self.after_cancel(self.notif_id) except ValueError: pass try: self.after_cancel(self.internet_id) except ValueError: pass # periodically checks if the internet connection is turned on self.internet_id = self.after(self.timeout, self.test_connection) else: # try to reconnect logging.exception(str(type(e))) run([ "notify-send", "-i", "dialog-error", _("Error"), traceback.format_exc() ]) self.logout(box, reconnect=True) except ValueError: # Error sometimes raised when a connection process is interrupted by a logout process pass