def get_sock_for_send_msg(user, to_uri, debug = False): """ Parse (ip, port) get by start_chat request, create a new socket object to register and invite buddy to start a online conversation. """ conversations = user.get_conversations() conv = conversations[to_uri] while not conv.start_chat_response: time.sleep(0.1) res_obj = conv.start_chat_response auth_val = res_obj.headers.get_field_value("A") ip, port, credential = _get_ip_port_credential_from_auth_field(auth_val) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conv.sock = sock sock.connect((ip, port)) call_id = conv.call_id sip_type = SIP(SIP.REGISTER) sid = user.sid headers = { "F" : sid, "I" : call_id, "Q" : "%d %s" % (DEFAULT_SIP_SEQUENCE, str(sip_type)), "K" : ["text/html-fragment", "multiparty", "nudge"], "A" : 'TICKS auth="%s"' % credential, } conn = SIPConnection(sock, sip_type = sip_type, headers = headers) conn.send(debug = debug) # invite_buddy_to_start_chat(user, call_id, to_uri, debug) sip_type = SIP(SIP.SERVICE) sip_event = SIPEvent(SIPEvent.INVITE_BUDDY) headers = { "F" : sid, "I" : call_id, "Q" : "%d %s" % (DEFAULT_SIP_SEQUENCE, str(sip_type)), "N" : str(sip_event), } body = '<args><contacts><contact uri="%s" /></contacts></args>' % to_uri conn = SIPConnection(sock, sip_type = sip_type, headers = headers, body = body) res_obj = conn.send(debug = debug) if res_obj.code != SIPResponse.OK: sock.close() logger.error("get sock for cat failed") return None # recevie option response, we can do detect user if is offline or hide from response buf = SIPConnection.recv(sock, debug = debug) res_obj = SIPResponse(buf) logger.info("user is offline: %s" % str(detect_user_if_is_offline_from_invite_response(res_obj))) if detect_user_if_is_offline_from_invite_response(res_obj): sock.close() return None assert res_obj != None # recevie conversation response buf = SIPConnection.recv(sock, debug = debug) res_obj = SIPResponse(buf) assert res_obj != None return res_obj
class ContactListWidow(QtGui.QWidget, Ui_ContactListWindow): FIRST_COLUMN = 0 def __init__(self, blah): QtGui.QWidget.__init__(self) self.setupUi(self) self.setWindowIcon(QtGui.QIcon(LOGO_PATH)) self.chat_history = {} self.blah = blah self.user = blah.user self.chat_win = ChatWindow(blah) self.chat_win.move(200, 200) self.load_user_presence_list() self.update_all_portraits_t = None # listen the socket (main sock) used in SIPC auth self.listen_main_sock_t = None self.server_push_data_list = [] self.connect(self, QtCore.SIGNAL("recved_server_push_datas()"), self.consume_server_push_datas) self.conversation_server_push_data_list = [] self.connect(self, QtCore.SIGNAL("recved_conversation_server_push_datas()"), self.consume_conversation_server_push_datas) self.setup_keep_alive_timer() self.setup_check_user_presence_timer() self._init_addition_widgets() i_subscribe_contact_list(self.user, debug=True) self.listen_main_sock_t_run() self.keep_alive_timer.start() def on_contact_list_double_clicked(self): contact = self.get_current_selected_contact() DOUBLE_CLICK_ON_CONTACT_ITEM = contact is not None if DOUBLE_CLICK_ON_CONTACT_ITEM: self.goto_or_create_tab_before_send_msg(contact) def _init_addition_widgets(self): self.nicknameLabel.setText(self.blah.get_my_name()) self.set_user_presence() self._init_portrait_widget() self.contact_list_tree_model = ContactListTreeModel( self.user.group_agent) self.contact_list_tree_view.setModel(self.contact_list_tree_model) self.selection_model = self.contact_list_tree_view.selectionModel() self.contact_list_tree_view.mouseDoubleClickEvent = contact_list_double_click_wrapper( self.contact_list_tree_view.mousePressEvent, self.contact_list_tree_view) self.connect(self.contact_list_tree_view, QtCore.SIGNAL('doubleClicked()'), self.on_contact_list_double_clicked) self.contact_list_tree_view.mouseReleaseEvent = contact_list_view_left_click_wrapper( self.contact_list_tree_view.mouseReleaseEvent, self.contact_list_tree_view) self.connect(self.contact_list_tree_view, QtCore.SIGNAL("leftClicked()"), self.on_contact_group_left_clicked) # self.connect(self.contact_list_tree_model, # QtCore.SIGNAL('update_contact_order()'), # self.update_contact_order_after_presence_changed) self.update_portraits_t_run() self.reportBugBtn.clicked.connect(self.on_report_bug_btn_click) self.add_buddy_btn.clicked.connect(self.on_add_buddy_btn_click) # def update_contact_order_after_presence_changed(self): # logger.info('data_change') def on_contact_group_left_clicked(self): idx = self.selection_model.currentIndex() obj = idx.internalPointer() if obj and obj.get_type() == GroupAgentItemType.GROUP: if self.contact_list_tree_view.isExpanded(idx): self.contact_list_tree_view.collapse(idx) else: self.contact_list_tree_view.expand(idx) def update_portraits_t_run(self): if self.update_all_portraits_t and not self.update_all_portraits_t.isFinished( ): return self.update_all_portraits_t = IThread(i_update_all_portraits, user=self.user) self.update_all_portraits_t.start() def get_current_selected_contact(self): idx = self.selection_model.currentIndex() obj = idx.internalPointer() if obj and obj.get_type() == GroupAgentItemType.CONTACT: return obj def on_avatar_left_clicked(self): read_only = True user = self.blah.user is_save_btn_clicked, new_user = ProfileDialog.get_data( self, user, read_only) assert new_user != None def _init_portrait_widget(self): self.set_portrait() self.portraitLabel.mousePressEvent = portrait_left_click_wrapper( self.portraitLabel.mousePressEvent, self.portraitLabel) self.connect(self.portraitLabel, QtCore.SIGNAL('leftClicked()'), self.on_avatar_left_clicked) self.set_portrait() def set_portrait(self, path=None): if not path: path = self.user.get_portrait_path() or DEFAULT_PORTRAIT_PATH elif path and not os.path.exists(path): self.download_portrait_t = IThread(i_download_portrait_by_uri, user=self.user, uri=self.user.uri, debug=True) self.connect(self.download_portrait_t, QtCore.SIGNAL("thread_finished()"), self.download_portrait_t_finished) self.download_portrait_t.start() pixmap = QtGui.QPixmap(path) width, height = 60, 60 pixmap = pixmap.scaled(QtCore.QSize(width, height)) self.portraitLabel.setPixmap(pixmap) def download_portrait_t_finished(self): res_obj = self.download_portrait_t.get_return() assert res_obj != None def on_add_buddy_btn_click(self): myname = "" # self.user.nickname[:10] send_btn_clicked, data = AddBuddyDialog.get_data(parent=self, name=myname) if send_btn_clicked: i_add_buddy(self.user, data["account"], data["name"], debug=True) def on_report_bug_btn_click(self): popup_about(self) def switch_presence_to_online(self): pass def switch_presence_to_away(self): pass def switch_presence_to_invisible(self): pass def switch_presence_to_offline(self): logger.error("in switch_presence_to_offline()") # there are bug in timer ? # QObject::startTimer: QTimer can only be used with threads started with QThread self.check_user_presence_timer.stop() self.keep_alive_timer.stop() convs = self.user.get_conversations() uri_list = convs.keys() for uri in uri_list: conv = convs[uri] conv.over() convs.pop(uri) sock = self.user.get_sock() sock.close() while not self.listen_main_sock_t.isFinished(): time.sleep(0.1) while not self.update_all_portraits_t.isFinished(): time.sleep(0.1) self.hide() self.blah.show() self.blah.show_or_hide_login_widgets(show=True) def load_user_presence_list(self): for const, presence in UserPresence.__dict__[ "CONST_TO_STR_ZH"].iteritems(): self.presenceComboBox.addItem(presence, QtCore.QVariant(const)) self.connect(self.presenceComboBox, QtCore.SIGNAL('currentIndexChanged ( int )'), self.on_presence_changed) def on_presence_changed(self, idx): presence_const = self.presenceComboBox.itemData(idx).toInt()[0] i_set_presence(self.user, presence_const) self.set_presence_icon(presence_const) def set_presence_icon(self, presence_const): #icon_path = os.path.join(ICON_PATH, "status-%s.png" % str(UserPresence(presence_const))) icon_path = ":/status-%s.png" % str(UserPresence(presence_const)) self.presenceLabel.setPixmap(QtGui.QPixmap(icon_path)) def set_user_presence(self): idx = self.presenceComboBox.findData( QtCore.QVariant(self.user.get_presence())) self.presenceComboBox.setCurrentIndex(idx) self.set_presence_icon(self.user.get_presence()) def check_user_presence(self): if self.user.get_presence() == UserPresence.OFFLINE: logger.error('user.get_presence() == UserPresence.OFFLINE is True') self.switch_presence_to_offline() def consume_server_push_datas(self): while len(self.server_push_data_list): res_obj, sock = self.server_push_data_list.pop() self._consume_datas(res_obj, sock) def consume_conversation_server_push_datas(self): while len(self.conversation_server_push_data_list): res_obj, sock = self.conversation_server_push_data_list.pop() self._consume_datas(res_obj, sock) def _consume_notification(self, res_obj, sock): assert (sock is not None) sip_event_str = res_obj.headers.get_field_value("N") sip_event = SIPEvent.get_const_by_str(SIPEvent, sip_event_str) events = get_conversation_events(res_obj) body_dom = minidom.parseString(res_obj.body) CONTACT_PRESENCE_CHANGED = sip_event == SIPEvent.PRESENCE_V4 and \ NotificationEvent.PRESENCE_CHANGED in events CONTACT_LEFT = sip_event == SIPEvent.CONVERSATION and \ NotificationEvent.USER_LEFT in events SYNC_USER_INFO_V4 = sip_event == SIPEvent.SYNC_USER_INFO_V4 and \ NotificationEvent.SYNC_USER_INFO in events BEEN_DISCONNECTED = sip_event == SIPEvent.REGISTRATION and \ NotificationEvent.DEREGISTRATION in events ADD_BUDDY_REFUSED = sip_event == SIPEvent.SYSTEM_NOTIFY_V4 and \ NotificationEvent.ADD_BUDDY_REFUSED in events USER_DYNAMICS_CHANGED = sip_event == SIPEvent.SYSTEM_NOTIFY_V4 and \ NotificationEvent.USER_DYNAMICS_CHANGED ADD_BUDDY_APPLICATION = sip_event == SIPEvent.CONTACT and \ NotificationEvent.ADD_BUDDY_APPLICATION in events convs = self.user.get_conversations() if CONTACT_PRESENCE_CHANGED: offline_uri_list = update_contacts_presence_from_response( self.user, body_dom) for uri in offline_uri_list: if uri in convs: conv = convs[uri] assert conv.sock != self.user.get_sock() conv.over() #convs.pop(uri) self.update_portraits_t_run() self.emit( QtCore.SIGNAL( 'dataChanged ( const QModelIndex & , const QModelIndex & )' )) elif CONTACT_LEFT: member_nodes = body_dom.getElementsByTagName("member") for member_node in member_nodes: uri = member_node.getAttribute("uri") conv = convs[uri] assert conv.sock != self.user.get_sock() conv.over() #convs.pop(uri) elif SYNC_USER_INFO_V4: self._consume_noti_sync_user_info_v4(res_obj) elif BEEN_DISCONNECTED: OFFLINE_ALERTS = "You have been disconnected" \ "as someone has signed in with your ID on another computer." \ "<br /><br />" \ "Please note that if this was not intentional, some may have stolen your passwrod. "\ "Please change your password." popup_error(self, OFFLINE_ALERTS) self.switch_presence_to_offline() #"Sign in again", "OK" elif ADD_BUDDY_REFUSED: uri, reason = i_parse_add_buddy_refused(body_dom) assert uri != reason logger.error( "User (URI: %s) refused your add buddy application, reason: %s" % (uri, reason)) elif ADD_BUDDY_APPLICATION: self._consume_add_buddy_application(res_obj) elif USER_DYNAMICS_CHANGED: pass def update_portraits(self): for contact in self.user.group_agent.get_all_contacts(): if contact.image_changed == YES: i_download_portrait_by_uri(self.user, contact.uri) contact.image_changed = NO def _consume_add_buddy_application(self, res_obj): body_dom = minidom.parseString(res_obj.body) app_data = i_parse_add_buddy_application(body_dom) sid = get_sid_from_uri(app_data["uri"]) SHOW_MSG_TPL = u"我是 %s (飞信号: %s ),想添加您为好友" show_msg = SHOW_MSG_TPL % (app_data["who"], sid) send_btn_clicked, data = ReplyAddBuddyDialog.get_data( parent=self, show_msg=show_msg) if send_btn_clicked: if data["accept"]: result = ReplyAddBuddyApplication.ACCEPT else: result = ReplyAddBuddyApplication.REFUSE else: result = ReplyAddBuddyApplication.IGNORE i_reply_add_buddy(self.user, res_obj, result, data["refuse_reason"], data["decline_add_req_forever"], debug=True) def _consume_noti_sync_user_info_v4(self, res_obj): body_dom = minidom.parseString(res_obj.body) contact_list_nodes = body_dom.getElementsByTagName("contact-list") if not contact_list_nodes: return assert len(contact_list_nodes) == 1 contact_list_node = contact_list_nodes[0] contact_list_version = contact_list_node.getAttribute("version") if self.user.contact_list_version != contact_list_version: self.user.contact_list_version = contact_list_version buddy_nodes = body_dom.getElementsByTagName("buddy") for buddy_node in buddy_nodes: attr = buddy_node.getAttribute user_id = attr("user-id") contact = self.user.group_agent.get_contact_by_user_id(user_id) # someone send add buddy application to you, # and you send buddy application before any reply, # contact will be not found in self.user.contact_list. if not contact: continue if attr("action") == "remove": logger.error("!!! Your buddy (uri: %s) %s you" % (contact.uri, attr("action"))) convs = self.user.conversations() conv = convs.get(contact.uri, None) if conv: assert conv.sock != self.user.get_sock() conv.over() del conv self.user.group_agent.remove_user_by_user_id(user_id) elif attr("action") == "add": logger.info("!!! Your buddy (uri: %s) %s you" % (contact.uri, attr("action"))) cat = Contact(user=self.blah.user) map_node_attr_to_obj(buddy_node, cat) self.user.contact_list.append(cat) cat.buddy_lists = attr("buddy-lists") cat.online_notify = attr("online-notify") cat.permission_values = attr("permission-values") def _consume_invitation(self, res_obj, sock): assert (sock is not None) """ TODO: ignore invitation will prevent from hide detecting. """ i_send_ack(res_obj, sock) attr = res_obj.headers.get_field_value from_uri = attr("F") conv = Conversation(from_uri) convs = self.user.get_conversations() convs[from_uri] = conv get_sock_for_recv_msg_thread = IThread(get_sock_for_recv_msg, user=self.user, res_obj=res_obj, debug=True) conv.get_sock_for_recv_msg_thread = get_sock_for_recv_msg_thread self.connect(conv.get_sock_for_recv_msg_thread, QtCore.SIGNAL("thread_finished()"), self.recv_msg_t_finished) conv.get_sock_for_recv_msg_thread.start() def recv_msg_t_finished(self): convs_d = dict() convs = self.user.get_conversations() for to_uri, conv in convs.iteritems(): if conv.get_sock_for_recv_msg_thread and conv.get_sock_for_recv_msg_thread.isFinished( ): res_obj = conv.get_sock_for_recv_msg_thread.get_return() conv.get_sock_for_recv_msg_thread = None if res_obj.code == SIPResponse.OK: assert conv.sock != self.user.get_sock() assert conv.listen_thread == None self.add_listener_for_conversation_sock(conv) convs_d[to_uri] = conv else: convs_d[to_uri] = conv self.user.set_conversations(convs_d) def add_listener_for_conversation_sock(self, conv): assert conv.sock != self.user.get_sock() conv.listen_thread = IThread( self.listen_sock, sock=conv.sock, data_list=self.conversation_server_push_data_list, signal='recved_conversation_server_push_datas()', debug=True) conv.listen_thread.start() conv.keep_conn_busy_thread = PeriodicExecutor(30, i_keep_connection_busy, user=self.user, sock=conv.sock) conv.keep_conn_busy_thread.start() def _consume_message(self, res_obj, sock): assert (sock is not None) attr = res_obj.headers.get_field_value from_uri = attr("F") logger.info("Get message from: %s" % from_uri) msg = rm_markups(to_unicode_obj(res_obj.body)) if from_uri != SERVICE_PROVIDER_URI: i_send_ack(res_obj, sock) contact = self.user.group_agent.get_contact_by_uri(from_uri) if not contact: """ This message send you before him delete you from his buddy list. """ name = u"陌生人 (飞信号: %s) " % get_sid_from_uri(from_uri) else: name = contact.get_display_name() else: name = to_unicode_obj("系统信息") self.goto_or_create_tab_after_received_msg(from_uri) chat_history_model = self.chat_history[from_uri] self.chat_win.append_to_chat_history(chat_history_model, name, msg, OTHER_LOG_TPL) def goto_or_create_tab_after_received_msg(self, from_uri): IS_SYSTEM_MSG = from_uri.find('sip:') == STR_NOT_FOUND if not IS_SYSTEM_MSG: TAB_ALREADY_EXISTS = self.chat_win.go_to_tab_by_uri(from_uri) if not TAB_ALREADY_EXISTS: self.chat_win.create_tab(from_uri) def _consume_datas(self, res_obj, sock): sip_type = res_obj.get_sip_method() if sip_type == SIP.NOTIFICATION: self._consume_notification(res_obj, sock) elif sip_type == SIP.SIPC_4_0: i_process_sipc_request_response(self.user, res_obj) elif sip_type == SIP.INVITATION: self._consume_invitation(res_obj, sock) elif sip_type == SIP.MESSAGE: self._consume_message(res_obj, sock) # elif sip_type == SIP.INCOMING: # process_incoming(user, res_obj) def send_keep_connection_busy(self, sock, debug=False): i_keep_connection_busy(self.user, sock, debug) # try: # i_keep_connection_busy(self.user, sock, debug) # except socket.error as (err_no, err_msg): # assert err_msg != None # # `socket.error: [Errno 22] Invalid argument` exception means disconnect # if err_no == errno.EINVAL: # self.user.get_presence() = OFFLINE # time.sleep(1) def send_keep_alive(self, debug=False): i_send_keep_alive(self.user, debug) # try: # i_send_keep_alive(self.user, debug) # except socket.error as (err_no, err_msg): # assert err_msg != None # # `socket.error: [Errno 22] Invalid argument` exception means disconnect # if err_no == errno.EINVAL: # self.user.get_presence() = OFFLINE # time.sleep(1) def setup_keep_alive_timer(self): self.keep_alive_timer = QtCore.QTimer() self.connect(self.keep_alive_timer, QtCore.SIGNAL('timeout()'), self.send_keep_alive) msec = 1000 * 70 self.keep_alive_timer.setInterval(msec) def setup_check_user_presence_timer(self): print " in setup_check_user_presence_timer() " self.check_user_presence_timer = QtCore.QTimer() self.connect(self.check_user_presence_timer, QtCore.SIGNAL('timeout()'), self.check_user_presence) msec = 1000 * 5 self.check_user_presence_timer.setInterval(msec) def listen_main_sock_t_run(self): """ TODO: offline-handler socket.error: [Errno 32] Broken pipe """ # listen the socket (main sock) used in SIPC auth self.listen_main_sock_t = IThread(self.listen_sock, sock=self.user.get_sock(), data_list=self.server_push_data_list, signal='recved_server_push_datas()', debug=True) self.listen_main_sock_t.start() # self.connect(self.listen_main_sock_t, QtCore.SIGNAL('thread_finished()'), self.switch_presence_to_offline) def listen_sock(self, sock, data_list, signal, debug=False): IS_MAIN_SOCK = sock is self.user.get_sock() if IS_MAIN_SOCK: is_main_sock = 'yes' else: is_main_sock = 'no' recv_buf = "" while True: try: readys = select.select([sock], [], [sock]) except select.error as (err_no, err_msg): # sock passive close but listen thread still running logger.error("!!! %s while select, is main sock: %s" % (err_msg, is_main_sock)) SOCK_CLOSED_T_RUNNING = (not IS_MAIN_SOCK) and (err_no == errno.EBADF) if SOCK_CLOSED_T_RUNNING: return SOCK_CLOSED_T_RUNNING except socket.error as (err_no, err_msg): logger.error("!!! %s while select, is main sock: %s" % (err_msg, is_main_sock)) DEREGISTERED = IS_MAIN_SOCK and (err_no == errno.EBADF) return DEREGISTERED input_ready = readys[0] except_ready = readys[2] if except_ready: logger.error("!!! Get exception while read socket") raise Exception("get exception while read socket") if input_ready: buf = "" try: """ NOTICE: don't set size_once less than 1024, or you will get errno.EAGAIN and this sock will couldn't read data again. """ buf = SIPConnection.recv(sock, size_once=1024, flags=socket.MSG_DONTWAIT, debug=debug) except socket.error as (err_no, err_msg): if err_no == errno.EAGAIN: logger.error( "!!! %s while sock.recv, is main sock: %s" % (err_msg, is_main_sock)) res = is_a_complete_response(recv_buf) CONTINUE_RECV = len( recv_buf ) != 0 and res == SIPResponseValidity.NOT_COMPLETE if CONTINUE_RECV: print "CONTINUE_RECV:", CONTINUE_RECV continue recv_buf += buf """Contact left conversation with a `UserLeft` push msg will be active close sock(i), this sock will receives nothing after `UserLeft`, so length of buf will be zero. This sock will be active close if get deregistered event type of registration notification, so length of buf will be zero too. """ if len(recv_buf) == 0: if not IS_MAIN_SOCK: logger.error( "!!! len(recv_buf) == 0, is main sock: %s", is_main_sock) return else: pkgs, remain = split_package(recv_buf) while len(pkgs): res_obj = pkgs.pop(0) data_list.append((res_obj, sock)) self.emit(QtCore.SIGNAL(signal)) recv_buf = remain