Merge changes from default branch into refactoring branch
This commit is contained in:
		
						commit
						01553e53c2
					
				
					 8 changed files with 114 additions and 106 deletions
				
			
		| 
						 | 
				
			
			@ -581,11 +581,10 @@ class MetacontactManager():
 | 
			
		|||
		#FIXME: can this append ?
 | 
			
		||||
		assert False
 | 
			
		||||
 | 
			
		||||
	def get_metacontacts_tags(self, account):
 | 
			
		||||
		'''return a list of tags for a given account'''
 | 
			
		||||
		if not account in self._metacontacts_tags:
 | 
			
		||||
			return []
 | 
			
		||||
		return self._metacontacts_tags[account].keys()
 | 
			
		||||
	def iter_metacontacts_families(self, account):		
 | 
			
		||||
		for tag in self._metacontacts_tags[account]:
 | 
			
		||||
			family = self._get_metacontacts_family_from_tag(account, tag)
 | 
			
		||||
			yield family
 | 
			
		||||
 | 
			
		||||
	def _get_metacontacts_tag(self, account, jid):
 | 
			
		||||
		'''Returns the tag of a jid'''
 | 
			
		||||
| 
						 | 
				
			
			@ -643,7 +642,7 @@ class MetacontactManager():
 | 
			
		|||
		tag = self._get_metacontacts_tag(account, jid)
 | 
			
		||||
		if not tag:
 | 
			
		||||
			return False
 | 
			
		||||
		meta_jids = self.get_metacontacts_jids(tag, accounts)
 | 
			
		||||
		meta_jids = self._get_metacontacts_jids(tag, accounts)
 | 
			
		||||
		return len(meta_jids) > 1 or len(meta_jids[account]) > 1
 | 
			
		||||
 | 
			
		||||
	def is_big_brother(self, account, jid, accounts):
 | 
			
		||||
| 
						 | 
				
			
			@ -651,12 +650,12 @@ class MetacontactManager():
 | 
			
		|||
		if family:
 | 
			
		||||
			nearby_family = [data for data in family
 | 
			
		||||
				if account in accounts]
 | 
			
		||||
			bb_data = self.get_metacontacts_big_brother(nearby_family)
 | 
			
		||||
			bb_data = self._get_metacontacts_big_brother(nearby_family)
 | 
			
		||||
			if bb_data['jid'] == jid and bb_data['account'] == account:
 | 
			
		||||
				return True
 | 
			
		||||
		return False
 | 
			
		||||
 | 
			
		||||
	def get_metacontacts_jids(self, tag, accounts):
 | 
			
		||||
	def _get_metacontacts_jids(self, tag, accounts):
 | 
			
		||||
		'''Returns all jid for the given tag in the form {acct: [jid1, jid2],.}'''
 | 
			
		||||
		answers = {}
 | 
			
		||||
		for account in self._metacontacts_tags:
 | 
			
		||||
| 
						 | 
				
			
			@ -673,9 +672,9 @@ class MetacontactManager():
 | 
			
		|||
		[{'account': acct, 'jid': jid, 'order': order}, ]
 | 
			
		||||
		'order' is optional'''
 | 
			
		||||
		tag = self._get_metacontacts_tag(account, jid)
 | 
			
		||||
		return self.get_metacontacts_family_from_tag(account, tag)
 | 
			
		||||
		return self._get_metacontacts_family_from_tag(account, tag)
 | 
			
		||||
 | 
			
		||||
	def get_metacontacts_family_from_tag(self, account, tag):
 | 
			
		||||
	def _get_metacontacts_family_from_tag(self, account, tag):
 | 
			
		||||
		if not tag:
 | 
			
		||||
			return []
 | 
			
		||||
		answers = []
 | 
			
		||||
| 
						 | 
				
			
			@ -765,8 +764,30 @@ class MetacontactManager():
 | 
			
		|||
		if account2 > account1:
 | 
			
		||||
			return -1
 | 
			
		||||
		return 0
 | 
			
		||||
	
 | 
			
		||||
	def get_nearby_family_and_big_brother(self, family, account):
 | 
			
		||||
		'''Return the nearby family and its Big Brother
 | 
			
		||||
 | 
			
		||||
	def get_metacontacts_big_brother(self, family):
 | 
			
		||||
		Nearby family is the part of the family that is grouped with the metacontact.
 | 
			
		||||
		A metacontact may be over different accounts. If accounts are not merged
 | 
			
		||||
		then the given family is split account wise.
 | 
			
		||||
 | 
			
		||||
		(nearby_family, big_brother_jid, big_brother_account)
 | 
			
		||||
		'''
 | 
			
		||||
		if common.gajim.config.get('mergeaccounts'):
 | 
			
		||||
			# group all together
 | 
			
		||||
			nearby_family = family
 | 
			
		||||
		else:
 | 
			
		||||
			# we want one nearby_family per account
 | 
			
		||||
			nearby_family = [data for data in family if account == data['account']]
 | 
			
		||||
 | 
			
		||||
		big_brother_data = self._get_metacontacts_big_brother(nearby_family)
 | 
			
		||||
		big_brother_jid = big_brother_data['jid']
 | 
			
		||||
		big_brother_account = big_brother_data['account']
 | 
			
		||||
 | 
			
		||||
		return (nearby_family, big_brother_jid, big_brother_account)
 | 
			
		||||
 | 
			
		||||
	def _get_metacontacts_big_brother(self, family):
 | 
			
		||||
		'''which of the family will be the big brother under wich all
 | 
			
		||||
		others will be ?'''
 | 
			
		||||
		family.sort(cmp=self._compare_metacontacts)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -462,7 +462,10 @@ class XMPPDispatcher(PlugIn):
 | 
			
		|||
		# we have released dispatcher, so self._owner has no methods
 | 
			
		||||
		if not res:
 | 
			
		||||
			return
 | 
			
		||||
		self._owner.remove_timeout()
 | 
			
		||||
		if 'remove_timeout' in self._owner.__dict__:
 | 
			
		||||
			# When we receive data after we started disconnecting, Transport may
 | 
			
		||||
			# already be plugged out
 | 
			
		||||
			self._owner.remove_timeout()
 | 
			
		||||
		for (_id, _iq) in self._expected.items():
 | 
			
		||||
			if _iq is None:
 | 
			
		||||
				# If the expected Stanza would have arrived, ProcessNonBlocking
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -645,44 +645,44 @@ class NonBlockingHTTP(NonBlockingTCP):
 | 
			
		|||
		if self.get_state() == PROXY_CONNECTING:
 | 
			
		||||
			NonBlockingTCP._on_receive(self, data)
 | 
			
		||||
			return
 | 
			
		||||
		if not self.recvbuff:
 | 
			
		||||
			# recvbuff empty - fresh HTTP message was received
 | 
			
		||||
			try:
 | 
			
		||||
				statusline, headers, self.recvbuff = self.parse_http_message(data)
 | 
			
		||||
			except ValueError:
 | 
			
		||||
				self.disconnect()
 | 
			
		||||
				return
 | 
			
		||||
			if statusline[1] != '200':
 | 
			
		||||
				log.error('HTTP Error: %s %s' % (statusline[1], statusline[2]))
 | 
			
		||||
				self.disconnect()
 | 
			
		||||
				return
 | 
			
		||||
			self.expected_length = int(headers['Content-Length'])
 | 
			
		||||
			if 'Connection' in headers and headers['Connection'].strip()=='close':
 | 
			
		||||
				self.close_current_connection = True
 | 
			
		||||
		else:
 | 
			
		||||
			#sth in recvbuff - append currently received data to HTTP msg in buffer
 | 
			
		||||
			self.recvbuff = '%s%s' % (self.recvbuff, data)
 | 
			
		||||
 | 
			
		||||
		if self.expected_length > len(self.recvbuff):
 | 
			
		||||
		# append currently received data to HTTP msg in buffer
 | 
			
		||||
		self.recvbuff = '%s%s' % (self.recvbuff or '', data)
 | 
			
		||||
		statusline, headers, httpbody, buffer_rest = self.parse_http_message(
 | 
			
		||||
			self.recvbuff)
 | 
			
		||||
		
 | 
			
		||||
		if not (statusline and headers and httpbody):
 | 
			
		||||
			log.debug('Received incomplete HTTP response')
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		if statusline[1] != '200':
 | 
			
		||||
			log.error('HTTP Error: %s %s' % (statusline[1], statusline[2]))
 | 
			
		||||
			self.disconnect()
 | 
			
		||||
			return
 | 
			
		||||
		self.expected_length = int(headers['Content-Length'])
 | 
			
		||||
		if 'Connection' in headers and headers['Connection'].strip()=='close':
 | 
			
		||||
			self.close_current_connection = True
 | 
			
		||||
			
 | 
			
		||||
		if self.expected_length > len(httpbody):
 | 
			
		||||
			# If we haven't received the whole HTTP mess yet, let's end the thread.
 | 
			
		||||
			# It will be finnished from one of following recvs on plugged socket.
 | 
			
		||||
			log.info('not enough bytes in HTTP response - %d expected, %d got' %
 | 
			
		||||
				(self.expected_length, len(self.recvbuff)))
 | 
			
		||||
			return
 | 
			
		||||
		else:
 | 
			
		||||
			# First part of buffer has been extraced and is going to be handled,
 | 
			
		||||
			# remove it from buffer
 | 
			
		||||
			self.recvbuff = buffer_rest
 | 
			
		||||
 | 
			
		||||
		# everything was received
 | 
			
		||||
		httpbody = self.recvbuff
 | 
			
		||||
			# everything was received
 | 
			
		||||
			self.expected_length = 0
 | 
			
		||||
 | 
			
		||||
		self.recvbuff = ''
 | 
			
		||||
		self.expected_length = 0
 | 
			
		||||
 | 
			
		||||
		if not self.http_persistent or self.close_current_connection:
 | 
			
		||||
			# not-persistent connections disconnect after response
 | 
			
		||||
			self.disconnect(do_callback=False)
 | 
			
		||||
		self.close_current_connection = False
 | 
			
		||||
		self.last_recv_time = time.time()
 | 
			
		||||
		self.on_receive(data=httpbody, socket=self)
 | 
			
		||||
		self.on_http_request_possible()
 | 
			
		||||
			if not self.http_persistent or self.close_current_connection:
 | 
			
		||||
				# not-persistent connections disconnect after response
 | 
			
		||||
				self.disconnect(do_callback=False)
 | 
			
		||||
			self.close_current_connection = False
 | 
			
		||||
			self.last_recv_time = time.time()
 | 
			
		||||
			self.on_receive(data=httpbody, socket=self)
 | 
			
		||||
			self.on_http_request_possible()
 | 
			
		||||
 | 
			
		||||
	def build_http_message(self, httpbody, method='POST'):
 | 
			
		||||
		'''
 | 
			
		||||
| 
						 | 
				
			
			@ -707,7 +707,7 @@ class NonBlockingHTTP(NonBlockingTCP):
 | 
			
		|||
			headers.append('Connection: Keep-Alive')
 | 
			
		||||
		headers.append('\r\n')
 | 
			
		||||
		headers = '\r\n'.join(headers)
 | 
			
		||||
		return('%s%s\r\n' % (headers, httpbody))
 | 
			
		||||
		return('%s%s' % (headers, httpbody))
 | 
			
		||||
 | 
			
		||||
	def parse_http_message(self, message):
 | 
			
		||||
		'''
 | 
			
		||||
| 
						 | 
				
			
			@ -716,20 +716,27 @@ class NonBlockingHTTP(NonBlockingTCP):
 | 
			
		|||
			headers - dictionary of headers e.g. {'Content-Length': '604',
 | 
			
		||||
				'Content-Type': 'text/xml; charset=utf-8'},
 | 
			
		||||
			httpbody - string with http body)
 | 
			
		||||
			http_rest - what is left in the message after a full HTTP header + body
 | 
			
		||||
		'''
 | 
			
		||||
		message = message.replace('\r','')
 | 
			
		||||
		# Remove latest \n
 | 
			
		||||
		if message.endswith('\n'):
 | 
			
		||||
			message = message[:-1]
 | 
			
		||||
		(header, httpbody) = message.split('\n\n', 1)
 | 
			
		||||
		header = header.split('\n')
 | 
			
		||||
		statusline = header[0].split(' ', 2)
 | 
			
		||||
		header = header[1:]
 | 
			
		||||
		headers = {}
 | 
			
		||||
		for dummy in header:
 | 
			
		||||
			row = dummy.split(' ', 1)
 | 
			
		||||
			headers[row[0][:-1]] = row[1]
 | 
			
		||||
		return (statusline, headers, httpbody)
 | 
			
		||||
		splitted = message.split('\n\n')
 | 
			
		||||
		if len(splitted) < 2:
 | 
			
		||||
			# no complete http message. Keep filling the buffer until we find one
 | 
			
		||||
			buffer_rest = message
 | 
			
		||||
			return ('', '', '', buffer_rest)
 | 
			
		||||
		else:
 | 
			
		||||
			(header, httpbody)  = splitted[:2]
 | 
			
		||||
			if httpbody.endswith('\n'):
 | 
			
		||||
				httpbody = httpbody[:-1]
 | 
			
		||||
			buffer_rest = "\n\n".join(splitted[2:])
 | 
			
		||||
			header = header.split('\n')
 | 
			
		||||
			statusline = header[0].split(' ', 2)
 | 
			
		||||
			header = header[1:]
 | 
			
		||||
			headers = {}
 | 
			
		||||
			for dummy in header:
 | 
			
		||||
				row = dummy.split(' ', 1)
 | 
			
		||||
				headers[row[0][:-1]] = row[1]
 | 
			
		||||
			return (statusline, headers, httpbody, buffer_rest)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonBlockingHTTPBOSH(NonBlockingHTTP):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -494,7 +494,7 @@ class P2PConnection(IdleObject, PlugIn):
 | 
			
		|||
			self.disconnect()
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
	def disconnect(self):
 | 
			
		||||
	def disconnect(self, message=''):
 | 
			
		||||
		''' Closes the socket. '''
 | 
			
		||||
		gajim.idlequeue.remove_timeout(self.fd)
 | 
			
		||||
		gajim.idlequeue.unplug_idle(self.fd)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2145,7 +2145,7 @@ class Interface:
 | 
			
		|||
		family = gajim.contacts.get_metacontacts_family(account, jid)
 | 
			
		||||
		if family:
 | 
			
		||||
			nearby_family, bb_jid, bb_account = \
 | 
			
		||||
				self.roster._get_nearby_family_and_big_brother(family, account)
 | 
			
		||||
				gajim.contacts.get_nearby_family_and_big_brother(family, account)
 | 
			
		||||
		else:
 | 
			
		||||
			bb_jid, bb_account = jid, account
 | 
			
		||||
		self.roster.select_contact(bb_jid, bb_account)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -604,29 +604,7 @@ class RosterWindow:
 | 
			
		|||
					big_brother_account=big_brother_account)
 | 
			
		||||
 | 
			
		||||
	def _get_nearby_family_and_big_brother(self, family, account):
 | 
			
		||||
		'''Return the nearby family and its Big Brother
 | 
			
		||||
 | 
			
		||||
		Nearby family is the part of the family that is grouped with the metacontact.
 | 
			
		||||
		A metacontact may be over different accounts. If regroup is s False the
 | 
			
		||||
		given family is split account wise.
 | 
			
		||||
 | 
			
		||||
		(nearby_family, big_brother_jid, big_brother_account)
 | 
			
		||||
		'''
 | 
			
		||||
		if self.regroup:
 | 
			
		||||
			# group all together
 | 
			
		||||
			nearby_family = family
 | 
			
		||||
		else:
 | 
			
		||||
			# we want one nearby_family per account
 | 
			
		||||
			nearby_family = [data for data in family
 | 
			
		||||
				if account == data['account']]
 | 
			
		||||
 | 
			
		||||
		big_brother_data = gajim.contacts.get_metacontacts_big_brother(
 | 
			
		||||
			nearby_family)
 | 
			
		||||
		big_brother_jid = big_brother_data['jid']
 | 
			
		||||
		big_brother_account = big_brother_data['account']
 | 
			
		||||
 | 
			
		||||
		return (nearby_family, big_brother_jid, big_brother_account)
 | 
			
		||||
 | 
			
		||||
		return gajim.contacts.get_nearby_family_and_big_brother(family, account)
 | 
			
		||||
 | 
			
		||||
	def _add_self_contact(self, account):
 | 
			
		||||
		'''Add account's SelfContact to roster and draw it and the account.
 | 
			
		||||
| 
						 | 
				
			
			@ -649,10 +627,8 @@ class RosterWindow:
 | 
			
		|||
 | 
			
		||||
		return contact
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	def redraw_metacontacts(self, account):
 | 
			
		||||
		for tag in gajim.contacts.get_metacontacts_tags(account):
 | 
			
		||||
			family = gajim.contacts.get_metacontacts_family_from_tag(account, tag)
 | 
			
		||||
		for family in gajim.contacts.iter_metacontacts_families(account):
 | 
			
		||||
			self._recalibrate_metacontact_family(family, account)
 | 
			
		||||
 | 
			
		||||
	def add_contact(self, jid, account):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -382,7 +382,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
 | 
			
		|||
		family = gajim.contacts.get_metacontacts_family(self.conn.name, jid)
 | 
			
		||||
		if family:
 | 
			
		||||
			nearby_family, bb_jid, bb_account = \
 | 
			
		||||
				gajim.interface.roster._get_nearby_family_and_big_brother(family,
 | 
			
		||||
				gajim.contacts.get_nearby_family_and_big_brother(family,
 | 
			
		||||
				self.conn.name)
 | 
			
		||||
		else:
 | 
			
		||||
			bb_jid, bb_account = jid, self.conn.name
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -227,9 +227,10 @@ class TestNonBlockingHTTP(AbstractTransportTest):
 | 
			
		|||
 | 
			
		||||
		data = "<test>Please don't fail!</test>"
 | 
			
		||||
		http_message = transport.build_http_message(data)
 | 
			
		||||
		statusline, headers, http_body = transport.parse_http_message(
 | 
			
		||||
		statusline, headers, http_body, buffer_rest = transport.parse_http_message(
 | 
			
		||||
			http_message)
 | 
			
		||||
 | 
			
		||||
		self.assertFalse(bool(buffer_rest))
 | 
			
		||||
		self.assertTrue(statusline and isinstance(statusline, list))
 | 
			
		||||
		self.assertTrue(headers and isinstance(headers, dict))
 | 
			
		||||
		self.assertEqual(data, http_body, msg='Input and output are different')
 | 
			
		||||
| 
						 | 
				
			
			@ -250,26 +251,26 @@ class TestNonBlockingHTTP(AbstractTransportTest):
 | 
			
		|||
		transport._on_receive(message)
 | 
			
		||||
		self.assertTrue(self.have_received_expected(), msg='Failed: In one go')
 | 
			
		||||
 | 
			
		||||
# FIXME: Not yet implemented.
 | 
			
		||||
#	def test_receive_http_message_in_chunks(self):
 | 
			
		||||
#		''' Let _on_receive handle some chunked http messages  '''
 | 
			
		||||
#		transport = self._get_transport(self.bosh_http_dict)
 | 
			
		||||
#		
 | 
			
		||||
#		header = ("HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n" +
 | 
			
		||||
#			"Content-Length: 88\r\n\r\n")
 | 
			
		||||
#		payload = "<test>Please don't fail!</test>"
 | 
			
		||||
#		body = "<body xmlns='http://jabber.org/protocol/httpbind'>%s</body>" \
 | 
			
		||||
#			% payload
 | 
			
		||||
#		message = "%s%s" % (header, body)
 | 
			
		||||
#
 | 
			
		||||
#		chunk1, chunk2, chunk3  = message[:20], message[20:73], message[73:]
 | 
			
		||||
#		nextmessage_chunk = "\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type: text/x"
 | 
			
		||||
#		chunks = (chunk1, chunk2, chunk3, nextmessage_chunk)
 | 
			
		||||
#
 | 
			
		||||
#		transport.onreceive(self.expect_receive(body, msg='Failed: In chunks'))
 | 
			
		||||
#		for chunk in chunks:
 | 
			
		||||
#			transport._on_receive(chunk)
 | 
			
		||||
#		self.assertTrue(self.have_received_expected(), msg='Failed: In chunks')
 | 
			
		||||
	def test_receive_http_message_in_chunks(self):
 | 
			
		||||
		''' Let _on_receive handle some chunked http messages  '''
 | 
			
		||||
		transport = self._get_transport(self.bosh_http_dict)
 | 
			
		||||
		
 | 
			
		||||
		header = ("HTTP/1.1 200 OK\r\nContent-Type: text/xml; charset=utf-8\r\n" +
 | 
			
		||||
			"Content-Length: 88\r\n\r\n")
 | 
			
		||||
		payload = "<test>Please don't fail!</test>"
 | 
			
		||||
		body = "<body xmlns='http://jabber.org/protocol/httpbind'>%s</body>" \
 | 
			
		||||
			% payload
 | 
			
		||||
		message = "%s%s" % (header, body)
 | 
			
		||||
 | 
			
		||||
		chunk1, chunk2, chunk3, chunk4  = message[:20], message[20:73], \
 | 
			
		||||
			message[73:85], message[85:]
 | 
			
		||||
		nextmessage_chunk = "\r\n\r\nHTTP/1.1 200 OK\r\nContent-Type: text/x"
 | 
			
		||||
		chunks = (chunk1, chunk2, chunk3, chunk4, nextmessage_chunk)
 | 
			
		||||
 | 
			
		||||
		transport.onreceive(self.expect_receive(body, msg='Failed: In chunks'))
 | 
			
		||||
		for chunk in chunks:
 | 
			
		||||
			transport._on_receive(chunk)
 | 
			
		||||
		self.assertTrue(self.have_received_expected(), msg='Failed: In chunks')
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
	unittest.main()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue