author | Alberto Bertogli
<albertogli@telpin.com.ar> 2005-04-09 18:50:42 UTC |
committer | Alberto Bertogli
<albertogli@telpin.com.ar> 2005-04-09 18:50:42 UTC |
parent | 34e8994a7ed1a37e1edd3da8abc2ef8002ee5349 |
msn | +81 | -81 |
msncb.py | +22 | -22 |
msnlib.py | +97 | -97 |
diff --git a/msn b/msn index 9e64de3..ecf8470 100644 --- a/msn +++ b/msn @@ -59,7 +59,7 @@ gren old new Renames the group "old" with the name "new" color [theme] Shows or set the color theme to "theme" close nick Closes the switchboard connection with "nick" config Shows the configuration -info [nick] Shows the user information and pending messages (if any), +info [nick] Shows the user information and pending messages (if any), or our personal info nick newnick Changes your nick to "newnick" privacy p a Sets whether accept messages from people not on your list (p) @@ -167,7 +167,7 @@ def print_list(md, only_online = 0, userlist = None, include_emails = 0): ul.sort() for email in ul: u = userlist[email] - if u.status != 'FLN': + if u.status != 'FLN': hl = 1 else: if only_online: continue @@ -215,7 +215,7 @@ def print_grouped_list(md, only_online = 0, include_emails = 0): if 'B' in u.lists: printl('[!]') printl('\n') - + def print_user_info(email): "Prints the user information, and pending messages" u = m.users[email] @@ -238,14 +238,14 @@ def print_user_info(email): if u.priv.has_key('typing') and u.priv['typing']: out += c.bold + 'Last typing at:\t' + c.normal out += time.asctime(time.localtime(u.priv['typing'])) + '\n' - if u.sbd: + if u.sbd: out += c.bold + 'Switchboard:\t' + c.normal + str(u.sbd) + '\n' if u.sbd.msgqueue: out += c.bold + 'Pending messages:' + '\n' for msg in u.sbd.msgqueue: out += c.bold + '\t>>> ' + c.normal + msg + '\n' printl(out) - + def print_prompt(): "Prints the user prompt" safe_write('\r' + c.red + c.bold + '[msn] ' + c.normal) @@ -288,7 +288,7 @@ def beep(q = 0): "Beeps unless it's told to be quiet" if not q: printl('\a') - + def safe_flush(): """Safely flushes stdout. It fixes a strange issue with flush and @@ -446,16 +446,16 @@ def get_config(file): def null(s): "Null function, useful to void debug ones" pass - + def log_msg(email, type, msg, mtime = 0, users = []): """Logs the message or event of the 'type', related to 'email', with the content 'msg', to a file in the specified directory. See documentation for more specific details, specially about formatting.""" - + if not config['log history']: return - + if config['profile']: prepend = config['profile'] + '::' else: @@ -491,7 +491,7 @@ def log_msg(email, type, msg, mtime = 0, users = []): out += '*** ' + msg + '\n' elif type == 'multi': out += '+++ ' + msg + '\n' - + fd = open(file, 'a') fd.write(out) fd.close() @@ -566,7 +566,7 @@ matchm_root = '' matchp_status = 0 matchp_root = '' -# input history buffer, to store previous commands. +# input history buffer, to store previous commands. # we use a list [buffer, pointer] to avoid namespace pollution inbuf_history = [[], -1] @@ -583,7 +583,7 @@ def stdin_read(): printl(out + '\n', c.green, 1) redraw_cli() return - + in_esc = 0 input = sys.stdin.read() @@ -622,14 +622,14 @@ def stdin_read(): inbuf_history[0].append(inbuf[:-1]) inbuf_history[1] = len(inbuf_history[0]) - 1 # moves the pointer start_from = 0 # reset tab completion - + safe_write(char) tmpbuf = inbuf inbuf = '' out = parse_cmd(tmpbuf) printl(out + '\n', c.green, 1) redraw_cli() - + elif char == '\b' or ord(char) == 127: # ^H / DEL inbuf = inbuf[:-2] redraw_cli() @@ -637,7 +637,7 @@ def stdin_read(): elif ord(char) == 21: # ^U inbuf = '' redraw_cli() - + elif char == '\t': # tab p = inbuf.split() @@ -761,12 +761,12 @@ def stdin_read(): inbuf = inbuf + pi + ' ' redraw_cli() - + elif ord(char) == 4: # EOT safe_write('\n') out = parse_cmd('') printl(out + '\n', c.green, 1) - + elif ord(char) == 27: # ESC # we use in_esc for escape secuenses (composed of # ESC + '[' + LETTER). 1 means got ESC, 2 means got @@ -774,11 +774,11 @@ def stdin_read(): # generic handling in_esc = 1 inbuf = inbuf[:-1] - + elif ord(char) < 32: # unhandled control msnlib.debug('Got weird char: %d' % ord(char)) redraw_cli_cond(char) - + else: # normal if not in_esc: redraw_cli_cond(char) @@ -859,14 +859,14 @@ def clear_line(): def parse_cmd(cmd): """Parses the commands introduced by the user. It's pretty long and boring, as expected.""" - + global c, last_sent, last_received # ugly but necesary - + if len(cmd) == 0: quit() elif len(cmd) == 1: return '' - + # cut trailing newline and clean up if cmd[-1] == '\n': cmd = cmd[:-1] @@ -882,7 +882,7 @@ def parse_cmd(cmd): if not cmd: return '' cmd = s[0] params = '' - + # parse if cmd == 'status': # change status @@ -893,32 +893,32 @@ def parse_cmd(cmd): out += '\tonline, away, busy, brb, phone, lunch, invisible or idle' return out return 'Status changed to: %s' % params - + elif cmd == 'q': # quit quit() - + elif cmd == 'reload': # reload callbacks reload(msncb) m.cb = msncb.cb() - + elif cmd == 'w': # list print_grouped_list(m) - + elif cmd == 'ww': # list, include emails print_grouped_list(m, include_emails = 1) - + elif cmd == 'wr': # reverse list print_list(m, userlist = m.reverse, include_emails = 1) - + elif cmd == 'e': # list (online only) print_list(m, only_online = 1) - + elif cmd == 'eg': print_grouped_list(m, only_online = 1) elif cmd == 'ee': print_grouped_list(m, only_online = 1, include_emails = 1) - + elif cmd == 'g': # list groups print_group_list(m) @@ -929,7 +929,7 @@ def parse_cmd(cmd): except: return 'Error parsing command' m._send(cmd, pars) - + elif cmd == 'debug': # enable/disable debugging p = params.split() if len(p) != 1: @@ -944,7 +944,7 @@ def parse_cmd(cmd): return 'Debugging enabled' else: return 'Unknown parameter - must be "on" or "off"' - + elif cmd == 'config': # show config variables keys = config.keys() keys.sort() @@ -955,7 +955,7 @@ def parse_cmd(cmd): printl(c.bold + var + ' = ' + c.normal + value + '\n') printl(c.bold + 'use_termios = ' + str(use_termios) + '\n') printl(c.bold + 'screensize = ' + str(winsize) + '\n') - + elif cmd == 'color': # configure/show colors p = params.split() if len(p) != 1: @@ -968,23 +968,23 @@ def parse_cmd(cmd): else: c = color_classes[p[0]]() return "Changed theme to " + p[0] - + elif cmd == 'close': # close a connection p = params.split() - if len(p) != 1: + if len(p) != 1: return 'Error parsing command' email = nick2email(p[0]) - if not email: + if not email: return 'Unknown nick (%s)' % p[0] if not m.users[email].sbd: return 'No socket opened for %s' % p[0] desc = str(m.users[email].sbd) m.close(m.users[email].sbd) return 'Closed socket %s' % desc - + elif cmd == 'privacy': # set privacy mode p = params.split() - if len(p) != 2: + if len(p) != 2: return 'Error parsing command' try: public = int(p[0]) @@ -994,7 +994,7 @@ def parse_cmd(cmd): except: return 'Error: both parameters must be 1 or 0' m.privacy(public, auth) - + elif cmd == 'lignore': # ignore a user locally p = params.split() if len(p) == 0: @@ -1005,21 +1005,21 @@ def parse_cmd(cmd): email = nick2email(p[0]) if not email: return 'Unknown nick (%s)' % p[0] - if email in ignored: + if email in ignored: return 'User is already being locally ignored' ignored.append(email) return 'User is now being locally ignored' - + elif cmd == 'lunignore': # unignore a locally ignored user p = params.split() if len(p) == 0: return 'Error parsing command' email = nick2email(p[0]) if email not in ignored: - return 'User is not being locally ignored' + return 'User is not being locally ignored' ignored.remove(email) return 'User is no longer locally ignored' - + elif cmd == 'block': p = params.split() if len(p) == 0: @@ -1029,7 +1029,7 @@ def parse_cmd(cmd): return 'Unknown nick (%s)' % p[0] m.userblock(email) return 'User %s blocked' % email - + elif cmd == 'unblock': p = params.split() if len(p) == 0: @@ -1039,15 +1039,15 @@ def parse_cmd(cmd): return 'Unknown nick (%s)' % p[0] m.userunblock(email) return 'User %s unblocked' % email - + elif cmd == 'add': # add a user p = params.split() - if len(p) == 0: + if len(p) == 0: return 'Error parsing command' - elif len(p) == 1: + elif len(p) == 1: email = nick = p[0] gid = '0' - elif len(p) == 2: + elif len(p) == 2: email = p[0] nick = p[1] gid = '0' @@ -1060,7 +1060,7 @@ def parse_cmd(cmd): if gid not in m.groups.keys(): return 'Unknown group' m.useradd(email, nick, gid) - + elif cmd == 'del': # delete a user p = params.split() if len(p) != 1: return 'Error parsing command' @@ -1068,7 +1068,7 @@ def parse_cmd(cmd): if not email: return 'Unknown nick (%s)' % p[0] m.userdel(email) - + elif cmd == 'ren': # rename a user p = params.split() if len(p) != 2: return 'Error parsing command' @@ -1079,12 +1079,12 @@ def parse_cmd(cmd): u = m.users[email] m.userdel(email) m.useradd(email, newnick, u.gid) - + elif cmd == 'gadd': # add a group p = params.split() if len(p) != 1: return 'Error parsing command' m.groupadd(p[0]) - + elif cmd == 'gdel': # delete a group p = params.split() if len(p) != 1: return 'Error parsing command' @@ -1099,7 +1099,7 @@ def parse_cmd(cmd): printl('User %s (%s) will be deleted\n' % \ (u.nick, e), bold = 1) m.groupdel(gid) - + elif cmd == 'gren': # rename a group p = params.split() if len(p) != 2: return 'Error parsing command' @@ -1110,7 +1110,7 @@ def parse_cmd(cmd): if gid not in m.groups.keys(): return 'Unknown group' m.groupren(gid, newname) - + elif cmd == 'invite': # invite a user to an existing sbd p = params.split() if len(p) != 3: return 'Error parsing command' @@ -1126,12 +1126,12 @@ def parse_cmd(cmd): if not dst_sbd: return 'No current chat with user %s' % dst m.invite(email, dst_sbd) - + elif cmd == 'nick': # change our nick if len(params) < 1: return 'Error parsing command' nick = params m.change_nick(nick) - + elif cmd == 'info': # user info p = params.split() if len(p) != 1: @@ -1156,10 +1156,10 @@ def parse_cmd(cmd): if not email: return 'Unknown nick (%s)' % str(p[0]) print_user_info(email) - + elif cmd == 'sync': # manual sync m.sync() - + elif cmd == 'h': # show history printl('Incoming Message History (last %d messages)\n' \ % config['history size'], c.green, 1) @@ -1198,7 +1198,7 @@ def parse_cmd(cmd): return 'Unable to send message: User is offline' if (m.status == 'FLN' or m.status == 'HDN') and not m.users[email].sbd: return 'Unable to send message: Not allowed when offline' - + r = m.sendmsg(email, msg) last_sent = email if r == 1: @@ -1214,12 +1214,12 @@ def parse_cmd(cmd): return 'Message too big' else: return 'Error %d sending message' % r - + elif cmd == 'help' or cmd == '?': return help else: return 'Unknown command, type "help" for help' - + return '' @@ -1302,7 +1302,7 @@ def cb_msg(md, type, tid, params, sbd): global last_received t = tid.split(' ') email = t[0] - + # parse lines = params.split('\n') headers = {} @@ -1317,13 +1317,13 @@ def cb_msg(md, type, tid, params, sbd): headers[type] = value eoh += 1 eoh +=1 - + # handle special hotmail messages if email == 'Hotmail': if not headers.has_key('Content-Type'): return hotmail_info = {} - + # parse the body for i in lines: i = i.strip() @@ -1333,7 +1333,7 @@ def cb_msg(md, type, tid, params, sbd): type = tv[0] value = tv[1].strip() hotmail_info[type] = value - + msnlib.debug(params) if headers['Content-Type'] == 'text/x-msmsgsinitialemailnotification; charset=UTF-8': newmsgs = int(hotmail_info['Inbox-Unread']) @@ -1351,7 +1351,7 @@ def cb_msg(md, type, tid, params, sbd): c.green, 1) printl('\r\tSubject: %s\n' % subject, c.green, 1) return - + if headers.has_key('Content-Type') and headers['Content-Type'] == 'text/x-msmsgscontrol': # the typing notices nick = email2nick(email) @@ -1380,10 +1380,10 @@ def cb_msg(md, type, tid, params, sbd): log_msg(email, 'in', string.join(lines[eoh:], '\n')) # append the message to the history, keeping it below the configured limit - if len(history_ring) > config['history size']: + if len(history_ring) > config['history size']: del(history_ring[0]) history_ring.append((time.time(), email, lines[eoh:])) - + last_received = email msncb.cb_msg(md, type, tid, params, sbd) m.cb.msg = cb_msg @@ -1415,7 +1415,7 @@ def cb_iro(md, type, tid, params, sbd): uid, ucount, email, realnick = p nick = email2nick(email) if not nick: nick = email - + if ucount == '1': # do nothing if we only have one participant pass @@ -1444,7 +1444,7 @@ def cb_err(md, errno, params): perror(desc) msncb.cb_err(md, errno, params) m.cb.err = cb_err - + # users add, delete and modify def cb_add(md, type, tid, params): t = params.split(' ') @@ -1547,9 +1547,9 @@ if not config: config['profile'] = profile # set the mandatory values -if config.has_key('email'): +if config.has_key('email'): m.email = config['email'] -else: +else: perror('Error: email not specified in config file\n') quit(1) @@ -1567,7 +1567,7 @@ else: # and the optional ones, setting the defaults if not present # history size -if not config.has_key('history size'): +if not config.has_key('history size'): config['history size'] = 10 else: try: @@ -1587,7 +1587,7 @@ else: config['input history size'] = 10 # initial status -if not config.has_key('initial status'): +if not config.has_key('initial status'): config['initial status'] = 'online' elif config['initial status'] not in msnlib.status_table.keys(): perror('unknown initial status, using default\n') @@ -1637,7 +1637,7 @@ if not config.has_key('encoding'): config['encoding'] = os.environ['LC_ALL'] elif os.environ.has_key('LANG') and os.environ['LANG']: config['encoding'] = os.environ['LANG'] - else: + else: config['encoding'] = 'iso-8859-1' m.encoding = config['encoding'] @@ -1708,14 +1708,14 @@ while 1: fds = select.select(infd, outfd, [], timeout) except KeyboardInterrupt: quit() - + if timeout and len(fds[0] + fds[1]) == 0: # timeout, set auto away if m.status == 'NLN': m.change_status('away') auto_away = 1 printl('\rAutomatically changing status to away\n', c.green, 1) - + for i in fds[0] + fds[1]: # see msnlib.msnd.pollable.__doc__ if i == sys.stdin: # auto away revival @@ -1728,7 +1728,7 @@ while 1: else: try: m.read(i) - + # see if we got all the user list, so we can # change our initial status (doing it earlier # as we used to seems to break things for some @@ -1741,8 +1741,8 @@ while 1: else: perror('\rError setting status: unknown status %s\n' % config['initial status']) - - + + except ('SocketError', socket.error), err: if i != m: if i.msgqueue: diff --git a/msncb.py b/msncb.py index a05047a..0839e9b 100644 --- a/msncb.py +++ b/msncb.py @@ -75,7 +75,7 @@ class cb: self.bye = cb_bye # switchboard user disconnect - + error_table = { -10: 'Local error', 200: 'Syntax error', @@ -136,7 +136,7 @@ def cb_unk(md, type, tid, params): debug('Error! unknown event type "%s"' % type) debug('params: ' + str(params)) - + def cb_chl(md, type, tid, params): "Handles the challenges" if type != 'CHL': raise 'CallbackMess', (md, type, params) @@ -154,7 +154,7 @@ def cb_ign(md, type, tid, params, nd = None): def cb_out(md, type, tid, params): "Server disconnected us" debug('!!! Server closed the connection: ' + params) - + def cb_iln(md, type, tid, params): "Handles a friend status change" @@ -183,7 +183,7 @@ def cb_nln(md, type, tid, params): email = t[0] if len(t) > 1: nick = urllib.unquote(t[1]) else: nick = '' - + md.users[email].status = status md.users[email].realnick = nick debug('FRIEND %s (%s) changed status to :%s:' % (nick, email, status)) @@ -222,15 +222,15 @@ def cb_syn(md, type, tid, params): t = params.split() if len(t) != 3: raise "SYNError" - + lver = int(t[0]) total = int(t[1]) ngroups = int(t[2]) - + md.syn_lver = lver md.syn_total = total md.syn_ngroups = ngroups - + def cb_lst(md, type, tid, params): p = params.split(' ') @@ -241,15 +241,15 @@ def cb_lst(md, type, tid, params): groups = p[2] else: groups = '0' - + # we only use one main group id gid = groups.split(',')[0] - + if email in md.users.keys(): user = md.users[email] else: user = msnlib.user(email, nick, gid) - + # the list mask is a bitmask, composed of: # FL: 1 # AL: 2 @@ -260,22 +260,22 @@ def cb_lst(md, type, tid, params): if listmask & 1: user.lists.append('F') md.users[email] = user - + # in reverse if listmask & 8: user.lists.append('R') md.reverse[email] = user - + # in allow if listmask & 2: user.lists.append('A') - + # in block if listmask & 4: user.lists.append('B') - + md.lst_total += 1 - + # save in the global last_lst the email, because BPRs might need it md._last_lst = email @@ -297,7 +297,7 @@ def cb_prp(md, type, tid, params): type = t[0] if len(t) > 1: param = urllib.unquote(t[1]) else: param = '' - + if type == 'PHH': md.homep = param elif type == 'PHW': md.workp = param elif type == 'PHM': md.mobilep = param @@ -305,7 +305,7 @@ def cb_prp(md, type, tid, params): def cb_add(md, type, tid, params): - "Handles a user add; both you adding a user and a user adding you" + "Handles a user add; both you adding a user and a user adding you" t = params.split(' ') type = t[0] if type == 'RL': @@ -387,14 +387,14 @@ def cb_rng(md, type, tid, params): port = int(port) hash = t[2] email = t[3] - + fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # we set the socket nonblocking so we don't block (duh!) on connect(); # it will be picked up later from the select loop and handled via the # main read() call, which you will have to see to find out the rest. fd.setblocking(0) fd.connect_ex((ip, port)) - + sbd = msnlib.sbd() sbd.fd = fd sbd.block = 0 @@ -417,7 +417,7 @@ def cb_xfr(md, type, tid, params): fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) fd.setblocking(0) # see cb_rng fd.connect_ex((ip, port)) - + # look for the sbd, matching the tid sbd = None for i in md.sb_fds: @@ -427,7 +427,7 @@ def cb_xfr(md, type, tid, params): if not sbd: debug('AIEEE: XFR without sbd!') raise 'XFRError', (type, tid, params) - + sbd.fd = fd sbd.block = 0 sbd.state = 'cp' @@ -492,7 +492,7 @@ def cb_ack(md, type, tid, params, sbd): def cb_nak(md, type, tid, params, sbd): "Get a message negative acknowledge" debug('NAK: tid:%s' % tid) - + def cb_bye(md, type, tid, params, sbd): "Handles a user sb disconnect" diff --git a/msnlib.py b/msnlib.py index 1bedaac..9339894 100644 --- a/msnlib.py +++ b/msnlib.py @@ -63,7 +63,7 @@ class user: self.sbd = None self.priv = {} self.lists = [] - + def __repr__(self): return '<user email:%s nick:"%s" gid:%s>' % (self.email, self.nick, self.gid) @@ -73,18 +73,18 @@ class sbd: """SwitchBoard Descriptor Used as a pseudo-fd to store per-switchboard connection information. The state is either one of (too many): - + [answer] cp connect pending (just came from rng) re ready (just came from connect) an waiting for answer reply - + [invite] xf waiting for xfr response (not even connected yet) us waiting for usr response ca waiting for cal response jo waiting for a join response - + es established (waiting in boredom) You will find more information in the doc directory. @@ -103,12 +103,12 @@ class sbd: # unique for consistency self.block = 1 # blocking state self.orig_tid = None # tid of the original XFR - + def __repr__(self): return '<sbd: emails:%s state:%s fd:%d endpoint:%s>' % \ (str(self.emails), self.state, \ self.fileno(), self.endpoint) - + def fileno(self): return self.fd.fileno() @@ -117,13 +117,13 @@ class sbd: self.tid = self.tid + 1 return str(self.tid - 1) - + class msnd: """MSN Descriptor This is the main and most important class; it represents a msn instance. - + It's, afaik, nonblocking (not through setblocking() but mainly because it forces a select() i/o model (which you would probably have used anyway, unless you think async/signal io worths the mess for a stupid @@ -131,10 +131,10 @@ class msnd: always succed. Note that we sanely assume that writes do not block. Yes yes, you can use poll() too =) - + The only blocking call is the login() which is in charge of doing the initial connection and setup, all the rest are cpu bound. - + Once you have created an instance you should assign an email and a password at least, then do the login and i recommend you to call sync after that (and everyonce in a while doesn't hurt either). Finally you @@ -153,12 +153,12 @@ class msnd: that should have come with this file; also the callback file has good working code. """ - + def __init__(self): self.fd = None # socket fd self.sb_fds = [] # switchboard fds self.tid = 1 # transaction id - + self.email = None # login email self.pwd = None # login pwd self.nick = None # nick @@ -169,7 +169,7 @@ class msnd: self.status = 'FLN' # status self.encoding = 'iso-8859-1' # local encoding - + self.lhost = LOGIN_HOST self.lport = LOGIN_PORT self.ns = (None, None) # notification server @@ -180,18 +180,18 @@ class msnd: self.syn_ngroups = 0 # qty. of groups from SYN self.lst_total = 0 # qty. of LSTs got - + self.cb = None # callbacks self.users = {} # forward user list self.reverse = {} # reverse user list self.groups = {} # group list - - + + def __repr__(self): return '<msnd object, fd:%s, email:%s, tid:%s>' % (self.fd, self.email, self.tid) - + def fileno(self): "Useful for select()" return self.fd.fileno() @@ -210,24 +210,24 @@ class msnd: except: return s - + def pollable(self): """Return a pair of lists of poll()/select()ables network descriptors (ie. they are not fds, but actually classes that implement fileno() methods, like this one and the sbd). We do it this way because then it's simpler to read(). - + The reason behind the tuple is that for connect-pending fds we need to wait for writing readiness, so we must tell the userspace so. Notice that it still goes with the read() path. Yes, it is a mess but i couldn't find anything better yet. It works, it's efficient; let's pretend it's correct =) - + It includes the main file descriptor, and all the switchboards connections; then you call self.read(fd) on what this returns, and magic happens.""" - + iwtd = [] owtd = [] iwtd.append(self) @@ -240,18 +240,18 @@ class msnd: else: # readable! iwtd.append(nd) return (iwtd, owtd) - - + + def get_tid(self): "Returns a valid tid as string" self.tid = self.tid + 1 return str(self.tid - 1) - - + + def _send(self, cmd, params = '', nd = None, raw = 0): """Sends a command to the server, building it first as a string; uses, if specified, the pseudo fd (it can be either - msnd or sbd).""" + msnd or sbd).""" if not nd: nd = self tid = nd.get_tid() @@ -263,8 +263,8 @@ class msnd: c = c + '\r\n' c = self.encode(c) return fd.send(c) - - + + def _recv(self, fd = None): "Reads a command from the server, returns (cmd, tid, params)" if not fd: @@ -275,15 +275,15 @@ class msnd: while c != '\n' and c != '': buf = buf + c c = fd.recv(1) - + if c == '': raise 'SocketError' - + buf = buf.strip() pbuf = buf.split(' ') - + cmd = pbuf[0] - + # it's possible that we don't have any params (errors being # the most common) so we cover our backs if len(pbuf) >= 3: @@ -295,11 +295,11 @@ class msnd: else: tid = '0' params = '' - + debug(str(fd.fileno()) + ' <<< ' + buf) return (cmd, tid, params) - - + + def _recvmsg(self, msglen, fd = None): "Read a message from the server, returns it" if not fd: @@ -311,10 +311,10 @@ class msnd: #debug(str(fd.fileno()) + ' <<< ' + buf) buf = buf + c left = left - len(c) - + return self.decode(buf) - - + + def submit_sbd(self, sbd): """Submits a switchboard descriptor to add to our list; it is also put on our global list. @@ -322,7 +322,7 @@ class msnd: Note that if there is no such user, we create it in order to be able to do operations on users that are not in our server list.""" - + self.sb_fds.append(sbd) email = sbd.emails[0] if email not in self.users.keys(): @@ -333,8 +333,8 @@ class msnd: self.close(self.users[email].sbd) self.users[email].sbd = sbd return - - + + def change_status(self, st): """Changes the current status to: online, away, busy, brb, phone, lunch, invisible, idle, offline""" @@ -342,8 +342,8 @@ class msnd: self.status = status_table[st] self._send('CHG', self.status) return 1 - - + + def privacy(self, public = 1, auth = 0): """Sets our privacy state. First parameter define if you get messages from everybody or only from people on your list; the @@ -354,22 +354,22 @@ class msnd: if auth: self._send('GTC', 'A') # ask for auth else: self._send('GTC', 'N') # let them add you - + return 1 - - + + def change_nick(self, nick): "Changes our nick" nick = urllib.quote(nick) self._send('REA', self.email + ' ' + nick) return 1 - - + + def sync(self): "Syncronizes the tables" self._send('SYN', '0') return 1 - + def useradd(self, email, nick = None, gid = '0'): "Adds a user" @@ -378,37 +378,37 @@ class msnd: self._send('ADD', 'AL ' + email + ' ' + nick) self._send('ADD', 'FL ' + email + ' ' + nick + ' ' + gid) return 1 - + def userdel(self, email): "Removes a user" self._send('REM', 'AL ' + email) self._send('REM', 'FL ' + email) return 1 - + def userblock(self, email): self._send('REM', 'AL ' + email) self._send('ADD', 'BL ' + email + ' ' + email) if 'B' not in self.users[email].lists: self.users[email].lists.append('B') - + def userunblock(self, email): self._send('REM', 'BL ' + email) self._send('ADD', 'AL ' + email + ' ' + email) if 'B' in self.users[email].lists: self.users[email].lists.remove('B') - + def groupadd(self, name): "Adds a group" name = urllib.quote(name) self._send('ADG', name + ' 0') return 1 - + def groupdel(self, gid): "Removes a group" self._send('RMG', gid) return 1 - + def groupren(self, gid, newname): newname = urllib.quote(newname) self._send('REG', gid + ' ' + newname) @@ -418,8 +418,8 @@ class msnd: "Disconnect from the server" self.fd.send('OUT\r\n') self.fd.close() - - + + def close(self, sb): "Closes a given sbd" self.sb_fds.remove(sb) @@ -430,30 +430,30 @@ class msnd: except: pass del(sb) - - + + def invite(self, email, sbd): "Invites a user into an existing sbd" self._send('CAL', email, nd = sbd) - + def login(self): "Logins to the server, really boring" # open socket self.fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.fd.connect((self.lhost, self.lport)) - + # version information self._send('VER', 'MSNP8 CVR0') - + r = self._recv() if r[0] != 'VER' and r[2][0:4] != 'MSNP8': raise 'VersionError', r - + # lie the version, just in case self._send('CVR', '0x0409 win 4.10 i386 MSNMSGR 5.0.0544 MSMSGS ' + self.email) self._recv() # we just don't care what we get - + # ask for notification server self._send('USR', 'TWN I ' + self.email) @@ -466,7 +466,7 @@ class msnd: self.ns = ns.split(':') self.ns[1] = int(self.ns[1]) self.ns = tuple(self.ns) - + # close the fd and reopen it on the ns self.fd.close() self.fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -477,19 +477,19 @@ class msnd: r = self._recv() if r[0] != 'VER' and r[2][0:4] != 'MSNP8': raise 'VersionError', r - + # lie the version, just in case self._send('CVR', '0x0409 win 4.10 i386 MSNMSGR 5.0.0544 MSMSGS ' + self.email) self._recv() # we just don't care what we get - + # auth: send user, get hash self._send('USR', 'TWN I ' + self.email) - + r = self._recv() if r[0] != 'USR': raise 'AuthError', r hash = string.split(r[2])[2] - + # get and use the passport id passportid = self.passport_auth(hash) self._send('USR', 'TWN S ' + passportid) @@ -499,7 +499,7 @@ class msnd: raise 'AuthError', r self.nick = string.split(r[2])[2] self.nick = urllib.unquote(self.nick) - + return 1 @@ -508,23 +508,23 @@ class msnd: authorization; it's a helper function for login""" import urllib import httplib - + # initial connection debug('PASSPORT begin') nexus = urllib.urlopen('https://nexus.passport.com/rdr/pprdr.asp') h = nexus.headers purl = h['PassportURLs'] - + # parse the info d = {} for i in purl.split(','): key, val = i.split('=', 1) d[key] = val - + # get the login server login_server = 'https://' + d['DALogin'] login_host = d['DALogin'].split('/')[0] - + # build the authentication headers ahead = 'Passport1.4 OrgVerb=GET' ahead += ',OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom' @@ -534,16 +534,16 @@ class msnd: ahead += 'ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1062764229,' ahead += 'kpp=1,kv=5,ver=2.1.0173.1,tpf=' + hash headers = { 'Authorization': ahead } - + # connect to the given server debug('SSL Connect to %s' % login_server) ls = httplib.HTTPSConnection(login_host) - + # make the request debug('SSL GET') ls.request('GET', login_server, '', headers) resp = ls.getresponse() - + # loop if we get redirects until we get a definitive answer debug('SSL Response %d' % resp.status) while resp.status == 302: @@ -555,7 +555,7 @@ class msnd: ls.request('GET', login_server, '', headers) resp = ls.getresponse() debug('SSL Response %d' % resp.status) - + # now we have a definitive answer, if it's not 200 (success) # just raise AuthError if resp.status != 200: @@ -569,12 +569,12 @@ class msnd: ainfo = resp.getheader('Authentication-Info') except: ainfo = resp.getheader('WWW-Authenticate') - + d = {} for i in ainfo.split(','): key, val = i.split('=', 1) d[key] = val - + passportid = d['from-PP'] passportid = passportid[1:-1] # remove the "'" return passportid @@ -587,7 +587,7 @@ class msnd: """ if not nd: nd = self - + # handle different stages of switchboard initialization if nd in self.sb_fds: # connect pending @@ -600,7 +600,7 @@ class msnd: nd.fd.setblocking(1) nd.block = 1 nd.state = 're' - + # need to send the answer to the remote invitation if nd.type == 'answer' and nd.state == 're': params = self.email + ' ' + nd.hash + ' ' + \ @@ -613,9 +613,9 @@ class msnd: self._send('USR', params, nd) nd.state = 'us' return - - - + + + r = self._recv(nd.fd) type = r[0] tid = r[1] @@ -642,7 +642,7 @@ class msnd: elif type == 'RMG': self.cb.rmg(self, type, tid, params) elif type == 'REG': self.cb.reg(self, type, tid, params) elif type == 'RNG': self.cb.rng(self, type, tid, params) - + elif type == 'IRO': self.cb.iro(self, type, tid, params, nd) elif type == 'ANS': self.cb.ans(self, type, tid, params, nd) elif type == 'XFR': self.cb.xfr(self, type, tid, params) @@ -654,20 +654,20 @@ class msnd: elif type == 'NAK': self.cb.nak(self, type, tid, params, nd) elif type == 'BYE': self.cb.bye(self, type, tid, params, nd) - + elif type == 'MSG': params = tid + ' ' + params mlen = int(r[2].split()[-1]) msg = self._recvmsg(mlen, nd.fd) self.cb.msg(self, type, params, msg, nd) - + else: # catch server errors - always numeric type try: errno = int(type) except: errno = None - + if errno: self.cb.err(self, errno, \ str(tid) + ' ' + str(params)) @@ -692,13 +692,13 @@ class msnd: if email and email not in self.users.keys(): self.users[email] = user(email) - + if len(msg) > 1500: return -2 - + if not sb: sb = self.users[email].sbd - + # we don't have a connection if not sb: sb = sbd() @@ -714,12 +714,12 @@ class msnd: sb.orig_tid = str(self.tid) self._send('XFR', 'SB') return 1 - + # it's not ready yet elif sb.state != 'es': sb.msgqueue.append(msg) return 1 - + # no more excuses, send it else: # we make a list with all the messages to send @@ -736,8 +736,8 @@ class msnd: params = 'A ' + str(msize) + '\r\n' + m self._send('MSG', params, sb, raw = 1) del(pend[0]) - + return 2 - - + +