| 1 | #!/usr/bin/python |
| 2 | |
| 3 | import os, sys, signal |
| 4 | from time import time |
| 5 | import socket, select |
| 6 | import pynotify |
| 7 | |
| 8 | if len(sys.argv) < 2: |
| 9 | sys.stderr.write("usage: cidnotify SERVER\n") |
| 10 | sys.exit(1) |
| 11 | server = sys.argv[1] |
| 12 | |
| 13 | pynotify.init("cidnotify") |
| 14 | |
| 15 | sk = None |
| 16 | notif = None |
| 17 | curnum = None |
| 18 | lastring = None |
| 19 | t2n = None |
| 20 | buf = "" |
| 21 | namebuf = "" |
| 22 | name = None |
| 23 | actab = [] |
| 24 | |
| 25 | signal.signal(signal.SIGCHLD, signal.SIG_IGN) |
| 26 | |
| 27 | try: |
| 28 | acfile = open(os.getenv("HOME") + "/phone/actab") |
| 29 | except IOError: |
| 30 | pass |
| 31 | else: |
| 32 | for line in acfile: |
| 33 | pos = line.find(" ") |
| 34 | if pos < 0: continue |
| 35 | actab += [(line[:pos], line[pos + 1:].strip())] |
| 36 | acfile.close() |
| 37 | |
| 38 | for family, socktype, proto, canonname, sockaddr in socket.getaddrinfo(server, 5001, 0, socket.SOCK_STREAM): |
| 39 | try: |
| 40 | sk = socket.socket(family, socktype, proto) |
| 41 | sk.connect(sockaddr) |
| 42 | except socket.error: |
| 43 | sk = None |
| 44 | continue |
| 45 | else: |
| 46 | break |
| 47 | |
| 48 | if sk is None: |
| 49 | sys.stderr.write("cidnotify: could not connect to " + server + "\n"); |
| 50 | sys.exit(1) |
| 51 | |
| 52 | def popen(*cmd): |
| 53 | rfd, wfd = os.pipe() |
| 54 | pid = os.fork() |
| 55 | if pid == 0: |
| 56 | os.dup2(wfd, 1) |
| 57 | os.close(rfd) |
| 58 | os.close(wfd) |
| 59 | os.execvp(cmd[0], cmd) |
| 60 | sys.exit(127) |
| 61 | os.close(wfd) |
| 62 | return pid, rfd |
| 63 | |
| 64 | def closet2n(): |
| 65 | global t2n |
| 66 | if t2n is not None: |
| 67 | try: |
| 68 | os.kill(t2n[0], signal.SIGINT) |
| 69 | except: |
| 70 | pass |
| 71 | os.close(t2n[1]) |
| 72 | t2n = None |
| 73 | |
| 74 | def close(): |
| 75 | global notif, curnum, lastring, name |
| 76 | closet2n() |
| 77 | if notif is not None: |
| 78 | notif.close() |
| 79 | notif = None |
| 80 | curnum = None |
| 81 | lastring = None |
| 82 | name = None |
| 83 | |
| 84 | def findac(num): |
| 85 | for ac, acn in actab: |
| 86 | if num[:len(ac)] == ac: |
| 87 | return acn |
| 88 | return None |
| 89 | |
| 90 | def notiftext(): |
| 91 | if curnum == "B10": |
| 92 | return "<i>Undisclosed number</i>" |
| 93 | text = "<b>Number</b>: " + curnum + "\n" |
| 94 | acn = findac(curnum) |
| 95 | if acn is not None: |
| 96 | text += "<b>Area code</b>: " + acn + "\n" |
| 97 | else: |
| 98 | text += "<b>Area code</b>: <i>Unknown</i>\n" |
| 99 | if name is None: |
| 100 | text += "<b>Name</b>: <i>Looking up...</i>\n" |
| 101 | elif name == "": |
| 102 | text += "<b>Name</b>: <i>No hits</i>\n" |
| 103 | else: |
| 104 | text += "<b>Name</b>: " + name + "\n" |
| 105 | return text.strip() |
| 106 | |
| 107 | def newnum(num): |
| 108 | global curnum, t2n, notif, lastring |
| 109 | close() |
| 110 | curnum = num |
| 111 | notif = pynotify.Notification("Phone call", notiftext()) |
| 112 | notif.set_timeout(0) |
| 113 | notif.show() |
| 114 | lastring = time() |
| 115 | if curnum.isdigit(): |
| 116 | t2n = popen("tel2name", num) |
| 117 | |
| 118 | try: |
| 119 | while True: |
| 120 | fds = [sk.fileno()] |
| 121 | if t2n is not None: |
| 122 | fds += [t2n[1]] |
| 123 | if lastring is not None: |
| 124 | timeout = lastring + 10 - time() |
| 125 | else: |
| 126 | timeout = None |
| 127 | fds = reduce(lambda s, l: s | set(l), |
| 128 | select.select(fds, [], fds, timeout), |
| 129 | set()) |
| 130 | |
| 131 | if sk.fileno() in fds: |
| 132 | ret = sk.recv(100) |
| 133 | if ret == "": sys.exit(0) |
| 134 | buf += ret |
| 135 | lines = buf.split("\n") |
| 136 | buf = lines[-1] |
| 137 | for line in lines[:-1]: |
| 138 | if line[0] == "N": |
| 139 | newnum(line[1:]) |
| 140 | elif line[0] == "R": |
| 141 | if lastring is not None: |
| 142 | lastring = time() |
| 143 | |
| 144 | if t2n is not None and t2n[1] in fds: |
| 145 | ret = os.read(t2n[1], 100) |
| 146 | namebuf += ret |
| 147 | if ret == "": |
| 148 | closet2n() |
| 149 | name = namebuf.strip() |
| 150 | namebuf = "" |
| 151 | if notif is not None: |
| 152 | notif.update("Phone call", notiftext()) |
| 153 | notif.show() |
| 154 | |
| 155 | if lastring is not None and lastring + 10 < time(): |
| 156 | close() |
| 157 | |
| 158 | except KeyboardInterrupt: |
| 159 | pass |