def ListContacts(request):
    """
    Called when "ListContacts" packet is received, 
    it keeps a list of suppliers OR customers and some extra info.
    I think this is a most important method here, call different code from here.
    """
    global _CentralStatusDict
    global legal_codes
    global OnListSuppliersFunc
    global OnListCustomersFunc

    data = request.Payload
    dhnio.Dprint(6, 'central_service.ListContacts\n%s' % data)
    words = data.split('\n', 1)
    if len(words) < 2:
        dhnio.Dprint(1, 'central_service.ListContacts ERROR wrong data packet [%s]' % str(request.Payload))
        return

    code = words[0]
    if not code in legal_codes:
        dhnio.Dprint(1, 'central_service.ListContacts ERROR wrong data in the packet [%s] '  % str(request.Payload))
        return

    current_contacts = []
    clist, tail = dhnio._unpack_list(words[1])

    fire_flag = code.startswith('f')
    ban_flag = code.startswith('b')
    contact_type = code.lower()[-1]
    error_flag = code[-1].islower()

    spaceDict = None
    if tail is not None:
        # extract info about who is alive at the moment
        onlineArray = ''
        spaceDict = dhnio._unpack_dict_from_list(tail)
        if spaceDict.has_key('online'):
            onlineArray = spaceDict.pop('online')
        for i in range(len(onlineArray)):
            if i < len(clist):
                if clist[i]:
                    _CentralStatusDict[clist[i]] = onlineArray[i]
        # extract info about contacts local ip
        # if they are in same LAN we need to connect to local IP, not external
        local_ips = {}
        i = 0
        while True:
            idurl_and_local_ip = spaceDict.get('localip%03d' % i, None)
            if idurl_and_local_ip is None:
                break
            try:
                contact_idurl, contact_local_ip = idurl_and_local_ip.split('|')
            except:
                break
            local_ips[contact_idurl] = contact_local_ip
            spaceDict.pop('localip%03d' % i)
            i += 1
            dhnio.Dprint(6, 'central_service.ListContacts got local IP for %s: %s' % (nameurl.GetName(contact_idurl), contact_local_ip))
        identitycache.SetLocalIPs(local_ips)

    #---suppliers
    if contact_type == 's':
        current_contacts = contacts.getSupplierIDs()
        contacts.setSupplierIDs(clist)
        eccmap.init()
        contacts.saveSupplierIDs()
        for supid in contacts.getSupplierIDs():
            if supid.strip() == '':
                error_flag = True
                break
        dhnio.Dprint(4, "central_service.ListContacts (SUPPLIERS) code:%s error:%s length:%d" % (str(code), str(error_flag), len(clist)))
        for oldidurl in current_contacts:
            if oldidurl:
                if oldidurl not in clist:
                    events.info('central_service', 'supplier %s were disconnected' % nameurl.GetName(oldidurl),)
                    misc.writeSupplierData(oldidurl, 'disconnected', time.strftime('%d%m%y %H:%M:%S'))
                    dhnio.Dprint(6, 'central_service.ListContacts supplier %s were disconnected' % nameurl.GetName(oldidurl))
        for newidurl in clist:
            if newidurl:
                if newidurl not in current_contacts:
                    transport_control.ClearAliveTime(newidurl)
                    misc.writeSupplierData(newidurl, 'connected', time.strftime('%d%m%y %H:%M:%S'))
                    events.info('central_service', 'new supplier %s connected' % nameurl.GetName(newidurl), '',)
                    dhnio.Dprint(6, 'central_service.ListContacts new supplier %s connected' % nameurl.GetName(newidurl))
        backup_control.SetSupplierList(clist)
        if not fire_flag and current_contacts != clist:
            dhnio.Dprint(6, 'central_service.ListContacts going to call suppliers')
            identitypropagate.suppliers(wide=True)
        if OnListSuppliersFunc is not None:
            OnListSuppliersFunc()

    #---customers
    elif contact_type == 'c':
        current_contacts = contacts.getCustomerIDs()
        contacts.setCustomerIDs(clist)
        contacts.saveCustomerIDs()
        if spaceDict is not None:
            dhnio._write_dict(settings.CustomersSpaceFile(), spaceDict)
            reactor.callLater(3, local_tester.TestUpdateCustomers)
        # if not fire_flag and current_contacts != clist:
        identitypropagate.customers(wide=True)
        dhnio.Dprint(4, "central_service.ListContacts (CUSTOMERS) code:%s error:%s length:%d" % (str(code), str(error_flag), len(clist)))
        for oldidurl in current_contacts:
            if oldidurl not in clist:
                events.info('central_service', 'customer %s were disconnected' % nameurl.GetName(oldidurl),)
                dhnio.Dprint(6, 'central_service.ListContacts customer %s were disconnected' % nameurl.GetName(oldidurl))
        for newidurl in clist:
            if newidurl not in current_contacts:
                transport_control.ClearAliveTime(newidurl)
                events.info('central_service', 'new customer %s connected' % nameurl.GetName(newidurl))
                dhnio.Dprint(6, 'central_service.ListContacts new customer %s connected' % nameurl.GetName(newidurl))
        if OnListCustomersFunc is not None:
            OnListCustomersFunc()

    #---fire_flag
    if fire_flag:
        if contact_type == 's':
            index = -1
            for index in range(len(clist)):
                if clist[index] != current_contacts[index]:
                    break
            if index >= 0:
                # we want to send our Identity to new supplier
                # and than ask a list of files he have
                # so it should start rebuilding backups immediately
                # right after we got ListFiles from him
                identitypropagate.single(clist[index], wide=True) 

    #---ban_flag
    if ban_flag:
        events.notify('central_service', 'you have negative balance, all your suppliers was removed', '',)
        dhnio.Dprint(2, 'central_service.ListContacts !!! you have negative balance, all your suppliers was removed !!!')

    #---error_flag
    if error_flag:
        #reactor.callLater(settings.DefaultNeedSuppliersPacketTimeOut(), SendRequestSuppliers)
        events.info('central_service', 'could not find available suppliers',
                     'Central server can not find available suppliers for you.\nCheck your central settings.\n',)
        dhnio.Dprint(2, 'central_service.ListContacts !!! could not find available suppliers !!!')

    #---send ack            
    transport_control.SendAck(request)

    #---automats
    if contact_type == 's':
        central_connector.A('list-suppliers', clist)
        fire_hire.A('list-suppliers', (current_contacts, clist))
        data_sender.A('restart')
    elif contact_type == 'c':
        central_connector.A('list-customers', clist)
        
    #---transport_udp
    if transport_control._TransportUDPEnable:
        import lib.transport_udp as transport_udp
        new_contacts = contacts.getContactsAndCorrespondents()
        transport_udp.ListContactsCallback(current_contacts, new_contacts)
def RealSendCustomers():
    dhnio.Dprint(8, "identitypropagate.RealSendCustomers")
    SendToIDs(contacts.getCustomerIDs(), HandleCustomersAck)
def customers(AckHandler=None, wide=False):
    dhnio.Dprint(6, 'identitypropagate.customers')
    return propagate(contacts.getCustomerIDs(), AckHandler, wide)
def FetchCustomers():
    return fetch(contacts.getCustomerIDs())