Example #1
0
    def connect(self, host, port, userName = None, password = None):
        ISocket.connect(self)

        log().log("using CONNECT tunnelling for %s:%d" % (host, port))
        request = "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n" %\
            (host, port, host, port)

        if userName is not None:
            r = b64encode("%s:%d" % (userName, password))
            raise 1

        request += "\r\n"
        ISocket.send(self, request)

        response = ISocket.read(self, 1024)
        log().log('HttpProxy got response: ' + response)
        status = response.split()[1]

        if status not in ["200"]:
            log().log('HttpProxy error: ' + response)
            raise Exception(response)

        log().log('HttpProxy connected')
Example #2
0
 def SetSockets(self):
     self.__chat_isocket = ISocket(uuid=self.__email, email_pass=self.__email_pass, server=False)
     self.__chat_isocket.bind(('0.0.0.0', CHATPORT))
     self.__chat_isocket.connect((self.__server_addr, CHATPORT), self.__email, True)
     self.__p2p_isocket = ISocket(uuid=self.__email, only='S', server=True)
     self.__p2p_isocket.bind(('0.0.0.0', P2PPORT))
     self.__p2p_isocket.connect((self.__server_addr, P2PPORT), self.__email, True)
     self.__video_isocket = ISocket(uuid=self.__email, only='S', server=True, pack=False)
     self.__video_isocket.bind(('0.0.0.0', VIDEOPORT))
     self.__video_isocket.connect((self.__server_addr, VIDEOPORT), self.__email, True)
     pass
Example #3
0
class IServer():
    def __init(self):
        '''
        gpg: gpg user id
        daemon: client(C) | server(S)
        '''
        self.__tinydb_conn = None
        self.__laveldb_conn = None
        self.__gpg_obj = None
        self.__ipinfo = {}
        self.__chat_isocket = None
        self.__chat_isocket = None
        self.__video_isocket = None
        self.__seqnums = {}  # {email/groupid:seqnum}
        self.__out_counts = 0
        pass

    def Start(self):
        args = load_config('S')
        pass


    def SetGPG(self, uuid: str, passpharse=None):
        self.__gpg_obj = IGPG(uuid, passpharse)
        pass


    def SetSockets(self):
        self.__chat_isocket = ISocket(uuid=self.__email, email_pass=self.__email_pass, server=False)
        self.__chat_isocket.bind(('0.0.0.0', CHATPORT))
        self.__chat_isocket.connect((self.__server_addr, CHATPORT), self.__email, True)
        self.__p2p_isocket = ISocket(uuid=self.__email, only='S', server=True)
        self.__p2p_isocket.bind(('0.0.0.0', P2PPORT))
        self.__p2p_isocket.connect((self.__server_addr, P2PPORT), self.__email, True)
        self.__video_isocket = ISocket(uuid=self.__email, only='S', server=True, pack=False)
        self.__video_isocket.bind(('0.0.0.0', VIDEOPORT))
        self.__video_isocket.connect((self.__server_addr, VIDEOPORT), self.__email, True)
        pass


    def SetDB(self, dbpath=''):
        if dbpath == '':
            dbpath = DB_DIR
        self.__tinydb_conn = TinyDBConnection(dbpath)
        self.__leveldb_conn = LevelDBConnection(dbpath)
        pass

    def SetIP(self):
        self.__ipinfo = ip.GetIPInfo()
        pass


    def GetDaemon(self):
        return self.__daemon
        pass


    def GetProtocol(self):
        return self.__protocol


    def __resolve(self, header: dict, payload=None):
        '''
        收到从ISocket传上来的header(dict)和payload,通过header里的Op字段选择对应的函数处理并把header和payload作为参数传递过去
        '''
        protocol = header['protocol']
        sign = header['sign']
        des = header['des']
        if protocol not in VALIED_VERSIONS:
            print('Version Error, valied versions: ', json.dumps(VALIED_VERSIONS))
            return False

        if des == self.__email:
            content = self.__gpg_obj.decrypt(header['extra']['content'], sign)
            header['extra']['content'] = content
        select = header['op'].split('/')[0]  # msg | user | file
        op_type = header['op'].split('/')[1]
        func_name = '__' + select + '_' + op_type
        threading.Thread(target=getattr(self, func_name), args=([header, payload])).start()
        pass

    def __load_folder(self):
        '''
        加载下载目录内的文件信息
        '''
        print('Download folder: ', self.__folder)
        self.__fileshare_obj = FileCenter(self.__folder)
        self.__fileshare_obj.Initfiles()
        pass


    def __prepare(self, args: dict, payload=b''):
        '''
        准备header, 如果发送模式为socket,对payload和extra进行加密,如果是mail模式, 只加密extra, 将设置好的header(dict)和payload(bytes)一起传到ISocket发送出去
        header:
            报文参数: 至少需要{op, src, des, method, seqnum, status}
        '''
        if 'src' not in args.keys():
            args['src'] = self.__email
        if 'des' not in args.keys():
            args['des'] = self.__server_email
        if 'status' not in args.keys():
            args['status'] = -1
        if 'method' not in args.keys():
            args['method'] = 'IChat'
        if 'protocol' not in args.keys():
            args['protocol'] = 'ICHAT/' + str(Version)

        src = args['src']
        des = args['des']
        method = args['method']
        header= {}
        header['protocol'] = args['protocol']
        header['src'] = src
        header['des'] = des
        header['status'] = args['status']
        header['method'] = method
        header['op'] = args['op']
        select = args['op'].split('/')[0]
        op_type = args['op'].split('/')[1]
        msg_type = args['type']  # u(user) | g(group)
        method = args['method']
        default = (des == self.__server_email)
        if select == 'msg':
            nickname = args['name']  # user name | group name
            uid = GetUid(nickname, des)
            recipient_keyid = self.__gpg_obj.get_keyid(uid)
            header['seqnum'] = self.__seqnums[des]
            # 加密extra中的content
            content = args['content']
            if src == self.__email:
                content = self.__gpg_obj.encrypt(json.dumps(args['content']).encode('utf_8'), recipient_keyid, True)
            header['extra'] = {'type': msg_type, 'content': content}
            pass
        elif select == 'user':
            nickname = args['name']  # user name | group name
            uid = GetUid(nickname, des)
            recipient_keyid = self.__gpg_obj.get_keyid(uid)
            header['seqnum'] = self.__out_counts
            content = args['content']
            if src == self.__email:
                content = self.__gpg_obj.encrypt(json.dumps(args['content']).encode('utf_8'), recipient_keyid, True)
            header['extra'] = {'type': msg_type, 'content': content}
            pass
        elif select == 'file':
            content = args['content']
            header['extra'] = {'type': msg_type, 'content': content}
            pass
        else:
            print('Error packtet type')
        self.__chat_isocket.send(header, payload, default, method)
        self.__out_counts += 1
        pass

    def __msg_text(self, header: dict, payload: bytes):
        '''
        服务器收到
        '''
        src = header['src']
        des = header['des']
        status = header['status']
        seqnum = header['seqnum']
        msg_type = header['extra']['type']  # g(group) | (user)
        content = header['extra']['content']
        # status: 0发送失败, 1是是接收, -1是发送请求
        if des == self.__email:
            if status != 0:
                document = {}
                if status == 1:
                    document = {'src': des, 'des': src, 'type': 'text', 'content': content, 'seqnum': seqnum}
                else:
                    document = {'src': src, 'des': des, 'type': 'text', 'content': content, 'seqnum': seqnum}

                self.__tinydb_conn.insert(document, src)
                if src == self.__cur_contact:
                    print('刷新消息到聊天窗口')
                    print('text:', content)
            else:
                print('发送失败')
        else:
            '''
            服务器保存的是加密过的消息内容
            '''
            document = {'src': src, 'des': des, 'type': 'text', 'content': content, 'seqnum': seqnum}

            print('转发从%s 到 %s的消息' % (src, des))
            if msg_type == 'user':
                self.__tinydb_conn.insert(document, src)
                self.__tinydb_conn.insert(document, des)
                if des in self.__online_users:
                    self.__chat_isocket.send(header, payload, False)
            else:
                groupid = des
                print('群消息转发')
                self.__tinydb_conn.insert(document, des)
                members = self.__tinydb_conn.search({'groupid': groupid, 'status': 'online'}, 'groups')
                for member in members:
                    self.__chat_isocket.send(header, payload, False)
        pass

    def __msg_pic(self, header: dict, payload: bytes):
        '''
        content: 图片文件名
        payload: 图片数据
        '''
        src = header['src']
        des = header['des']
        status = header['status']
        seqnum = header['seqnum']
        msg_type = header['extra']['type']
        content = header['extra']['content']
        if des == self.__email:
            if status != 0:
                pic_path = self.___folder + '/pic/' + content
                with open(pic_path, 'wb+') as f:
                    print('Output to ', content)
                    f.write(payload)
                    pass

                document = {}
                if status == 1:
                    document = {'src': des, 'des': src, 'type': 'pic', 'content': content, 'seqnum': seqnum}
                else:
                    document = {'src': src, 'des': des, 'type': 'pic', 'content': content, 'seqnum': seqnum}

                self.__tinydb_conn.insert(document, src)
                if src == self.__cur_contact:
                    print('刷新消息到聊天窗口')
                    print('pic:', content)
            else:
                print('发送失败')
        else:
            '''
            服务器保存的是加密过的消息内容
            '''
            document = {'src': src, 'des': des, 'type': 'text', 'content': content, 'seqnum': seqnum}
            self.__tinydb_conn.insert(document, src)
            self.__tinydb_conn.insert(document, des)
            print('转发从%s 到 %s的消息' % (src, des))
            if msg_type == 'user':
                self.__tinydb_conn.insert(document, src)
                self.__tinydb_conn.insert(document, des)
                if des in self.__online_users:
                    self.__chat_isocket.send(header, payload, False)
            else:
                groupid = des
                print('群消息转发')
                self.__tinydb_conn.insert(document, des)
                members = self.__tinydb_conn.search({'groupid': groupid, 'status': 'online'}, 'groups')
                for member in members:
                    self.__chat_isocket.send(header, payload, False)
        pass

    def __msg_file(self,header: dict, payload: bytes):
        '''
        服务器处理客户端的发送文件请求,并且根据文件md5检查本地是否有,如果本地没有(设置status=0),有则status=1,直接发送给接收者
        payload: 文件md5
        '''
        src = header['src']
        des = header['des']
        seqnum = header['seqnum']

        fileinfo = header['extra']['content']
        filemd5 = fileinfo['md5']
        filename = fileinfo['name']

        check_local = self.__fileshare_obj.CheckExist(filemd5)
        reply = header
        reply['src'] = self.__email
        reply['des'] = src
        if not check_local:
            reply['status'] = 0
            print('No Local', filemd5)
            ok = self.__fileshare_obj.Download(fileinfo)
        else:
            reply['status'] = 1
            print('Local exists')

        document = {'src': src, 'des': des, 'seqnum': seqnum, 'type': 'file', 'name': filename, 'md5': filemd5}
        self.__tinydb_conn.insert(document, src)
        self.__tinydb_conn.insert(document, des)
        print('转发从%s 到 %s的文件' % (src, des))
        # 回复发送者
        self.__prepare(reply, payload, False)
        # 转发
        self.__prepare(header, payload, False)
        pass


    def __msg_video(self, header: dict, payload: bytes):
        src = header['src']
        des = header['des']
        seqnum = header['seqnum']
        extra = header['extra']
        finish = extra['finish']
        if not finish:
            if des != self.__email:
                print('转发视频')
            else:
                print()
            pass
        else:
            self.__video_isocket.delconn(src)
            if des != self.__email:
                self.__video_isocket.delconn(des)
        pass


    def __s_user_register(self,header: dict, payload: bytes):
        '''
        payload: {GPG_keyid, nickname}
        '''
        payload = payload.decode('utf_8')
        user_info = json.loads(payload)
        user_email = header['src']
        nickname = user_info['nickname']
        keyid = user_info['gpgkeyid']

        if user_email in self.__users.keys():
            header['status'] = 0
            header['extra']['error'] = 'Email already registered'
            self.__chat_isocket.send(header, payload, False)
            return

        results = self.__gpg_obj.recv(keyid)
        status = len(results) > 0
        if status:
            header['status'] = 1
            document = {'email': user_email, 'nickname': nickname, 'keyid': keyid}
            self.__tinydb_conn.insert(document, 'users')
        else:
            header['status'] = 0
            header['extra']['error'] = 'Invalied gpg keyid'
        self.__chat_isocket.send(header, payload, False)



    def __s_user_login(self,header: dict, payload: bytes):
        '''
        ModNum default = 1510
        '''

        src = header['src']
        nounce = header['extra']['nounce']
        status = ((sum(int(ch) for ch in src) + nounce) % ModNum == 0)
        header['status'] = int(status)
        if status:
            document = {'email': src, 'nounce': nounce}
            self.__online_users[src] = nounce
        self.__chat_isocket.send(header, payload, False)

        if status:
            print('返回用户联系人(邮件+用户名)列表,未读消息')
            print('返回群列表(群名字,群id),未读消息')
        pass



    def __user_logout(self, header: dict, payload: bytes):
        src = header['src']
        nounce = header['extra']['nounce']
        status = ((sum(int(ch) for ch in src) + nounce) % ModNum == 0)
        if not status:
            header['extra']['error'] = 'Check Nounce failed'

        haslogin = src in self.__online_users.keys()
        if not haslogin:
            header['extra']['error'] = '%s has logout' % src

        nounce_timeout = haslogin and self.__online_users[src] != nounce

        if nounce_timeout:
            header['extra']['error'] = 'Timeout nounce'

        status = status and nounce_timeout
        header['status'] = int(status)
        if status:
            del self.__online_users[src]
            print('成功注销:', src)
        self.__chat_isocket.send(header, payload, False)
        pass


    def __c_user_adduser(self, header: dict, payload: bytes):
        src = header['src']
        des = header['des']
        nickname = header['extra']['nickname']
        remark = header['extra']['remark']
        status = header['status']
        if status == -1:
            print('%s(%s) want to add you!(%s)' % (nickname, src, remark))
            # 弹出窗口显示请求者的邮箱,用户名和备注信息,并赋值给agree
            document = {'src': src, 'nickname': nickname, 'type': 'adduser', 'remark': remark}
            self.__tinydb_conn.insert(document, 'notify')
        elif status == 1:
            print('%s(%s) 同意添加为好友' % (nickname, src))
            self.__contacts[src] = nickname
        else:
            print('%s(%s) 拒绝添加为好友' % (nickname, src))
        pass


    def __user_adduser(self, header: dict, payload: bytes):
        src = header['src']
        des = header['des']
        status = header['status']
        if status == 1:
            document = {'user': src, 'contact': des}
            self.__tinydb_conn.insert(document, 'relations')
            document = {'user': des, 'contact': src}
            self.__tinydb_conn.insert(document, 'relations')
            self.__chat_isocket.send(header, payload, False)
        else:
            self.__chat_isocket.send(header, payload, False)


    def __user_deluser(self, header: dict, payload:bytes):
        src = header['src']
        des = header['des']
        status = header['status']
        if status == -1:
            self.__tinydb_conn.delete({'user': src, 'contact': des})
            self.__tinydb_conn.delete({'user': des, 'contact': src})
            header['status'] = 1
            self.__chat_isocket.send(header, payload, False)
        pass

    def __user_addgroup(self, header: dict, payload: bytes):
        '''
        des是群主邮箱
        '''
        src = header['src']
        des = header['des']
        status = header['status']
        groupid = header['extra']['groupid']
        groupname = header['extra']['groupname']
        if status == 1:
            document = {'group': groupid, 'user': des}
            self.__tinydb_conn.insert(document, 'relations')
            document = {'user': des, 'group': des}
            self.__tinydb_conn.insert(document, 'relations')
            self.__chat_isocket.send(header, payload, False)
        else:
            self.__chat_isocket.send(header, payload, False)
        pass


    def __user_delgroup(self, header:dict, payload:bytes):
        '''
        des是群主邮箱
        '''
        src = header['src']
        des = header['des']
        status = header['status']
        groupid = header['extra']['groupid']
        groupname = header['extra']['groupname']

        if status == -1:
            self.__tinydb_conn.delete({'user': src, 'group': groupid})
            self.__tinydb_conn.delete({'group': groupid, 'user': src})
            header['status'] = 1
            header['src'] = self.__email
            header['des'] = src
            self.__chat_isocket.send(header, payload, False)
        pass

    def __file_upload(self, header: dict, payload: bytes):
        src = header['src']
        des = header['des']
        status = header['status']
        filemd5 = header['extra']['md5']
        if status == -1:
            other_ip = header['extra']['ip']
            print('连接对方上传')
            tmp_conn = self.__p2p_isocket.connect(other_addr=(other_ip, P2PPORT), keepalive=0)
            self.__fileshare_obj.Upload(tmp_conn, filemd5)
        elif status == 1:
            print('上传成功')
        else:
            print('上传失败')
        pass

    def __file_download(self, header: dict, payload: bytes):
        '''
        header['extra']: {ipinfo:{'internal', 'outernal', 'trace', 'port'}, deepth:int, reaced:list, direct: 0 | 1, 'filemd5':str}
        '''
        print('搜索拥有该文件并且请求者能直接连接或者被直接连接的的其他用户')
        src = header['src']
        des = header['des']
        file_sharers = header['extra']
        status = header['status']
        broadcast = header['extra']['broadcast']
        extra = header['extra']
        ipinfo = extra['ipinfo']
        deepth = extra['deepth']
        reached = extra['reached']
        filemd5 = extra['filemd5']

        exist_not_full = self.__fileshare_obj.CheckExist(filemd5) and not self.__fileshare_obj.isFull(filemd5)
        if status == -1 and exist_not_full:
            if extra['direct']:
                newsocket = self.__p2p_isocket.connect((ipinfo['outernal'], ipinfo['port']), src)
                if newsocket:
                    header['status'] = 1
                    header['extra']['full'] = 0
                    newsocket.send(header, payload, False)

            elif self.__isdirect:
                print('叫对方连接自己')
                header['src'] = self.__email
                header['des'] = src
                header['status'] = 0
                header['extra']['in'] = 0
                self.__p2p_isocket.send(header, payload)
            else:
                res = ip.CompareIPS(self.__ipinfo['trace'], ipinfo['trace'])
                if res == 1:
                    print('连接对方')
                    newsocket = self.__p2p_isocket.connect((ipinfo['internal'], P2PPORT), src)
                    if newsocket:
                        header['status'] = 1
                        header['extra']['full'] = 0
                        newsocket.send(header, payload, False)
                elif res == 0:
                    print('叫对方连接自己')
                    header['src'] = self.__email
                    header['des'] = src
                    header['status'] = 0
                    header['extra']['in'] = 1
                    self.__p2p_isocket.send(header, payload)
        elif status == 0:
            full = extra['full']
            self_full = self.__fileshare_obj.isFull(filemd5)
            header['extra']['full'] = self_full
            if not self_full:
                isin = extra['in']
                new_conn = None
                if isin:
                    new_conn = self.__p2p_isocket.connect((ipinfo['internal'], P2PPORT), src)
                else:
                    new_conn = self.__p2p_isocket.connect((ipinfo['outernal'], P2PPORT), src)
                if new_conn:
                    print('P2P连接成功')
            else:
                print('满线程下载')
        else:
            full = extra['full']
            self_full = self.__fileshare_obj.isFull(filemd5)
            if (not full) and self_full:
                header['src'] = des
                header['des'] = src
                header['extra']['full'] = 1
                self.__p2p_isocket.send(header, payload, False)
                time.sleep(0.5)
                self.__p2p_isocket.delconn(src)
            elif full:
                self.__p2p_isocket.delconn(src)
            else:
                print('P2P connected')

        if (not full) and (self.__email not in reached) and (status == -1):
            print('广播')
            if header['deepth'] > 0:
                header['deepth'] -= 1
                header['reaced'].append(self.__email)
                self.__fileshare_obj.broadcast(header, payload)
        pass


    def __file_block(self, header:dict, payload:bytes):
        src = header['src']
        des = header['des']
        header['src'] = des
        header['des'] = src

        block_info = header['extra']
        status = header['status']
        block_data = None
        if status == -1:
            print('Send block to ', src)
            block_data = self.__fileshare_obj.TakeBlock(block_info)
            if not block_data:
                header['status'] = 0
                self.__chat_isocket.send(header, payload, False)
                return
            header['status'] = 1
            self.__chat_isocket.send(header, block_data, False)
            return

        elif status == 0:
            print('%s hasn\'t this block' % src)
        else:
            print('Get block from ', src)
            self.__fileshare_obj.AddBlock(block_info, payload, src)
        pass


    def __file_updatelist(self, header: dict, payload: bytes):
        src = header['src']
        des = header['des']
        filemd5 = header['extra']['md5']
        file_sharers = header['extra']['sharers']
        self.__fileshare_obj.UpdateList(filemd5, file_sharers)
        pass
Example #4
0
    def connect(self, host, port, userName = None, password = None):
        ISocket.connect(self)

        buf = chr(0x05)
        if userName is not None:
            buf += chr(0x02) # two methods
            buf += chr(0x00) # no authentication
            buf += chr(0x02) # username/password authentication
        else:
            buf += chr(0x01)
            buf += chr(0x00)

        ISocket.send(self, buf)

        buf = ISocket.read(self, 2)
        if ord(buf[0]) != 0x05 or ord(buf[1]) == 0xff:
            log().log("Bad answer from SOCKS5 proxy: " + ashex(buf))
            raise Exception("Bad answer from SOCKS5 proxy")

        if userName is not None:
            log().log("Authentification...")

            buf = '\x01' + struct.pack('!B', len(userName)) + userName
            buf += struct.pack('!B', len(password)) + password

            ISocket.send(self, buf)

            buf = ISocket.read(self, 2)
            print len(buf)
            if len(buf) != 2 or ord(buf[0]) != 0x01 or ord(buf[1]) != 0x00:
                log().log("Bad answer from SOCKS5 proxy: " + ashex(buf))
                raise Exception("Bad answer from SOCKS5 proxy")

        # Version, CONNECT, reserved, address type: host name
        buf = '\x05\x01\x00\x03'

        # Then actual data
        buf += struct.pack('!B', len(host)) + host
        buf += struct.pack('!H', port)

        ISocket.send(self, buf)

        buf = ISocket.read(self, 100)

        if ord(buf[0]) != 0x05 or ord(buf[1]) != 0x00:
            log().log("Bad answer from SOCKS5 proxy: " + ashex(buf))
            raise Exception("Bad answer from SOCKS5 proxy")

        log().log('Socks5Proxy connected')
Example #5
0
 def __init__(self, host, port, default_charset = 'cp1251'):
     ISocket.__init__(self, host, port, default_charset)