imapsave
imapsave
simply copies the content of an imap account into mbox text files (one file per folder). It does not do incremental backups.
I wrote imapsave
when I realized imapbackup could not use SSL encryption.
#!/usr/bin/env python # -*- coding: iso-8859-1 -*- # Licensed under the Python License (see http://www.python.org/psf/license/) # Copyright (C) 2008 Pierre Duquesne <stackp@online.fr> # # Changelog: # 20080722 * disable spinning when output is redirected to a file # 20080718 * Use BODY.PEEK[] in the fetch command as specified in RFC 3501 # (fix by Stefan) # 20080313 * Initial release import getpass import imaplib import email import email.Parser import re import sys import getopt from os.path import basename USAGE = """\ usage: %s [--ssl] [--port num] [--password pass] [server] [user] server The hostname of your imap server user Your login --ssl Use an encrypted connection -p num --port num Use port number num instead of default port (Defaults are 143 for unencrypted connection and 993 for ssl) -P pass --password pass Password for the server """ % basename(sys.argv[0]) class Spinner: """A class to show some activity on a character terminal.""" def __init__(self, message=''): self.message = message self.symbols = list('-\|/') self.nsym = len(self.symbols) self.n = 0 def spin(self): print '\r', self.message + self.symbols[self.n], sys.stdout.flush() self.n = (self.n + 1) % self.nsym def stop(self, stop_message=''): print '\r', self.message + stop_message def set_message(self, message): self.message = message class DummySpinner: """A class that mock Spinner and do not spin. Used when output is not a character terminal (e.g. a log file). """ def __init__(self, message=''): self.message = message def spin(self): pass def stop(self, stop_message=''): print self.message + stop_message def set_message(self, message): self.message = message def foldname_to_filename(folder): """Deal with weird characters in folder names.""" filename = re.sub('"(.*)"', ur'\1', folder) filename = filename.replace('/', '.') return filename try: # Parse command line arguments try: opts, args = getopt.gnu_getopt(sys.argv[1:], 'pP:h', ['port=', 'password=', 'ssl', 'help']) except getopt.GetoptError, e: print 'Error:', e sys.exit(1) ssl = False port = None password = None for o, a in opts: if o == '--ssl': ssl = True if o == '-p' or o == '--port': port = int(a) if o == '-P' or o == '--password': password = a elif o in ['-h', '--help']: print USAGE sys.exit(0) try: servername = args[0] except IndexError: servername = raw_input("Enter the server hostname: ") try: username = args[1] except IndexError: username = raw_input("Enter your username: ") # Connect print 'Connecting to %s as user %s ...' % (servername, username) if ssl: IMAP = imaplib.IMAP4_SSL else: IMAP = imaplib.IMAP4 try: if port: server = IMAP(servername, port) else: server = IMAP(servername) if not password: password = getpass.getpass() server.login(username, password) except Exception, e: print 'Error:', e sys.exit(1) # Retrieve folder list folders = [] foldptn = re.compile('\([^\)]*\) "[^"]*" ([^"]*)') for fold_desc in server.list()[1]: folder = foldptn.sub(ur'\1', fold_desc) folders.append(folder) # Save messages in a separate file for each folder if sys.stdout.isatty(): spinner = Spinner() else: spinner = DummySpinner() parser = email.Parser.Parser() succeed = [] failed = [] for folder in folders: msg = '%s ... ' % folder spinner.set_message(msg) try: resp, info = server.select(folder) if resp != 'OK': raise imaplib.IMAP4.error(' - '.join(info)) filename = foldname_to_filename(folder) fp = open(filename, 'w') resp, items = server.search(None, "ALL") numbers = items[0].split() for num in numbers: resp, data = server.fetch(num, "(BODY.PEEK[])") text = data[0][1] mess = parser.parsestr(text) fp.write(mess.as_string(unixfrom=True)) spinner.spin() succeed.append(folder) spinner.stop('Done.') fp.close() except imaplib.IMAP4.error, e: failed.append(folder) spinner.stop('Error! (' + str(e) + ')') server.logout() # Print the folders which failed, if any if failed != []: import textwrap wrapper = textwrap.TextWrapper(initial_indent=' ', subsequent_indent=' ') print print 'WARNING - The following folders could not be saved:' print wrapper.fill(', '.join(failed)) sys.exit(1) except KeyboardInterrupt: print '' print '^C received, stopping.'
CSS+Javascript pulsating progress bar
A bit like spinner but for the web in html+css+javascript. No ajax involved. It just pulsates perpetually:
<style type="text/css"> /* inspired by http://digitalbush.com/projects/progress-bar-plugin */ /* progress bar container */ #progressbar{ border:1px solid black; width:200px; height:20px; position:relative; } /* color bar */ #progress{ position:absolute; left: 20px; width:10%; height:100%; overflow:hidden; background-color:#369; } </style> <script language="JavaScript"> marg = 0 inc = 10 function update() { e = document.getElementById("progress"); e.style.left = marg + "%"; marg = (marg + inc) % 100 ; if (marg == 0 || marg == (100 - inc)) {inc = - inc;} setTimeout(update, 150); } onload=update; </script> <div id="progressbar"> <div id="progress"> </div></div>
Spinner: display activity
This was inspired by some code found in imapbackup. Sometimes you need to display activity on the text console to inform the user that the program is actually doing something.
That is the purpose of this small python class. The test()
function shows the usage. Here’s what test()
displays:
# Licensed under the Python License (see http://www.python.org/psf/license/) # Copyright (C) 2008 Pierre Duquesne <stackp@online.fr> import sys class Spinner: """A class to show a spinning ascii animation on a character terminal. It informs the user that some processing is being done. """ def __init__(self, message=''): self.message = message self.symbols = list('-\|/') self.nsym = len(self.symbols) self.n = 0 def spin(self): print '\r', self.message + self.symbols[self.n], sys.stdout.flush() self.n = (self.n + 1) % self.nsym def stop(self, stop_message=''): print '\r', self.message + stop_message def set_message(self, message): self.message = message class DummySpinner: """A class that mock Spinner and do not spin. To be used when output is not a character terminal (e.g. a log file). For example:: if sys.stdout.isatty(): s = Spinner() else: s = DummySpinner() """ def __init__(self, message=''): self.message = message def spin(self): pass def stop(self, stop_message=''): print self.message + stop_message def set_message(self, message): self.message = message if __name__ == "__main__": import time def test(): if sys.stdout.isatty(): s = Spinner() else: s = DummySpinner() # simple usage for i in range(10): s.spin() time.sleep(0.1) s.set_message('Initialization ... ') for i in range(10): s.spin() time.sleep(0.1) s.stop('Done') s.set_message('Loading ... ') for i in range(10): s.spin() time.sleep(0.1) s.stop('Done') test()
wifiscan
On linux, display the wireless networks around with a bit of shell magic:
#!/bin/sh # 20101123: Update to match the new iwlist output format. # 20080304: Initial release. IWLIST=/sbin/iwlist # List wireless interfaces ifaces=`LANG=C $IWLIST scan 2>/dev/null | grep "Scan completed" | awk '{print $1}' | tr '\n' ' '` # Choose only the first one iface=`echo $ifaces | awk '{print $1}'` # Extract informations and format output (echo ESSID~~~Channel~~~Quality~~~Encryption && \ echo "---~~~---~~~---~~~---" && \ $IWLIST $iface scan | \ sed -n -e 's/.*ESSID:\(.*\)/\1/p' \ -e 's/.*Channel:\([^ ]*\)/~~~\1/p' \ -e 's/Encryption key:\(.*\)/~~~\1/p' \ -e 's/.*Quality:\([^ ]*\)\/100.*/~~~\1/p' | \ xargs -n4 echo) | \ \ awk -F~~~ '{print $1 "~~~" $4 "~~~" $3 "~~~" $2}' | \ column -ntx -s ~~~
Here’s the result:
pierre@abricot:~$ wifiscan ESSID Encryption Quality Channel --- --- --- --- kameha on 88 1 freephonie on 88 1 on 90 1 NEUF_2714 on 75 6 Neuf WiFi off 75 6 on 88 1 Livebox-074c on 38 10 whiterabbit on 38 11 on 38 11
A better minimal web server in python
The simple web server from the python standard library is easy to improve to:
- answer several requests at the same time, and
- cancel a connection when the client stops responding.
#!/usr/bin/env python import SimpleHTTPServer, BaseHTTPServer, SocketServer, socket class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer) : """ New features w/r to BaseHTTPServer.HTTPServer: - serves multiple requests simultaneously - catches socket.timeout and socket.error exceptions (raised from RequestHandler) """ def __init__(self, *args): BaseHTTPServer.HTTPServer.__init__(self,*args) def process_request_thread(self, request, client_address): """ Overrides SocketServer.ThreadingMixIn.process_request_thread in order to catch socket.timeout """ try: self.finish_request(request, client_address) self.close_request(request) except socket.timeout: print 'Timeout during processing of request from', print client_address except socket.error, e: print e, 'during processing of request from', print client_address except: self.handle_error(request, client_address) self.close_request(request) class TimeoutHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): """ Abandon request handling when client has not responded for a certain time. This raises a socket.timeout exception. """ # Class-wide value for socket timeout timeout = 3 * 60 def setup(self): "Sets a timeout on the socket" self.request.settimeout(self.timeout) SimpleHTTPServer.SimpleHTTPRequestHandler.setup(self) def main(): try: BaseHTTPServer.test(TimeoutHTTPRequestHandler, ThreadedHTTPServer) except KeyboardInterrupt: print '^C received, shutting down server' if __name__ == '__main__': main()