acmecert: Fix cryptography bugs.
[utils.git] / cidnotify
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