Remove custom FQDN/SSL and export CID blacklist?

RobG

Customer
Joined
Nov 20, 2023
Messages
12
Reaction score
2
Upgraded from 18 to 20 in October last year and gave up at the time trying to install custom SSL cert. Have muddled through with mobile apps not working since then, but need to get this system 100% again.

Backed up 20 and tried to re-install last night, thinking I could choose to (reluctantly) ditch the custom FQDN during install, but this wasn't an option.

Now thinking of rebuilding the system from scratch as there are a few quirks in the system which has been backed up and restored for upgrades for the last 15 years (yes, we've used 3cx since 2011).

So 2 questions...

1) Can I remove the FQDN from a backup? This would be the most painless option for me in the short term

2) How to export the CID blacklist? Over 15 years we have lots of numbers in the system. There is no export option like there is for IP blacklist

Thanks,
Robert
 
So if I back up from the command line without the custom FQDN I can then restore and accept 3CX FQDN/SSL?
 
So if I back up from the command line without the custom FQDN I can then restore and accept 3CX FQDN/SSL?
Do you mean that you will use a 3CX FQDN instead of a custom one?
 
Yes, I don't have the time or energy these days to keep fighting and accept that for an easy life I'll have to use a 3CX FQDN.
 
The solution provided is to not include the FQDN and the License key information on the backup.
You will then need to disconnect the FQDN from the License key by logging in to your portal and following the steps below.


To disconnect the FQDN:
  1. Log in to the 3CX Portal with the subscription registration email address.
  2. Go to My Systems >> My Subscriptions.
  3. Find the subscription from which the FQDN needs to be disconnected and click Manage.
  4. Go to the Product section >> FQDN Disconnect.
  5. Click Disconnect to confirm.

attachment


Then you can start a new installation with the License Key and it will ask you to configure a new FQDN.
 
Many thanks, will be a couple of week now before I can look at this.
 
Thank you, glad to be of assistance.
 
Bad that this happened to you (since we always use the FQDN chosen by the customer), but to change it you may do the following:
https://www.3cx.com/community/threads/to-change-fqdn-for-nfr-license.135620/post-648936
Thanks for the information. With V18, it was easy to install snapd, then certbot, install my own certificate and run a cron job to keep it updated. The minimal debian install in V20 just wants to fight me all the time. If you can suggest a way to reliably install and update a custom certificate then I'm all ears :)
 
bad
then certbot
even bad

install my own certificate and run a cron job to keep it updated.
Maybe, but not with certbot and other packages outside 3CX repo (since 3CX v18 u3 or so) on the 3CX itself.

f you can suggest a way to reliably install and update a custom certificate then I'm all ears
we use this (or something like that) since many years on nearly all customer 3CX:
https://www.3cx.de/forum/threads/ssl-certificate-erneuert.114018/post-441718
 
About CID Blacklist:

1770195405817.png

...and here's a SIMPLE Python script:

Python:
#!/usr/bin/env python3
import json, requests

# --- CONFIGURATION ---
FQDN = "example.com"  # Change to your 3CX PBX FQDN
CLIENT_ID = "client01"  # Change to your Integrations -> API -> Client ID
CLIENT_SECRET = "abcdefghijabcdefghijabcdefghij"  # Change to your Integrationa -> API -> Client ID -> API Key

def get_token():
  url = f"https://{FQDN}/connect/token"
  data = {
    "grant_type": "client_credentials",
    "client_id": CLIENT_ID,
    "client_secret": CLIENT_SECRET
  }

  response = requests.post(url, data=data)
  response.raise_for_status()
  return response.json()["access_token"]

def request_headers():
  hh = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json"
  }
  return hh

def get_BlackListNumbers(token):
  url = f"https://{FQDN}/xapi/v1/BlackListNumbers"

  response = requests.get(url, headers=request_headers())

  if response.ok:
         response_data = response.json()
  else:
    print(f"Response Code: {response.status_code}\nResponse Text: {response.text}")
    error_report = {
      "Error": [
        {
          "Response Code": response.status_code,
          "Response Body": response.text
        }
      ]
    }
    return error_report, False #dictionary response
  return response_data, True #dictionary response

token = get_token()

data, success = get_BlackListNumbers(token)
if not success:
  print("Stopping process due to error:")
  print(json.dumps(data, indent=2))
  exit()

blackListNumbers_list = data["value"]

print(json.dumps(blackListNumbers_list, indent=2))
 
Hello,

Great work Kevin!
Here is a Windows Powershell script with the same Kevin code, and added export option that can be started form your own Windows PC, in a PowerShell. Just make sure you addchange the login credentials for your own server.
Just copy the code to a file (example "list_cid_blacklist.ps1"), than start powershell in that directory where the file is located, and start it with .\list_cid_blacklist.ps1

Code:
# --- CONFIGURATION ---
$FQDN = "example.com"  # Change to your 3CX PBX FQDN
$CLIENT_ID = "client01"  # Change to your Integrations -> API -> Client ID
$CLIENT_SECRET = "abcdefghijabcdefghijabcdefghij"  # Change to your Integrations -> API -> Client ID -> API Key
$EXPORT_PATH = ".\blacklist_export"  # Base path for exports (current directory)

function Get-Token {
    $url = "https://$FQDN/connect/token"
    
    $body = @{
        grant_type = "client_credentials"
        client_id = $CLIENT_ID
        client_secret = $CLIENT_SECRET
    }
    
    try {
        $response = Invoke-RestMethod -Uri $url -Method Post -Body $body -ContentType "application/x-www-form-urlencoded"
        return $response.access_token
    }
    catch {
        Write-Error "Failed to get token: $($_.Exception.Message)"
        exit 1
    }
}

function Get-RequestHeaders {
    param(
        [string]$Token
    )
    
    $headers = @{
        "Authorization" = "Bearer $Token"
        "Content-Type" = "application/json"
    }
    
    return $headers
}

function Get-BlackListNumbers {
    param(
        [string]$Token
    )
    
    $url = "https://$FQDN/xapi/v1/BlackListNumbers"
    $headers = Get-RequestHeaders -Token $Token
    
    try {
        $response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
        return @{data = $response; success = $true}
    }
    catch {
        $statusCode = $_.Exception.Response.StatusCode.value__
        $responseBody = $_.Exception.Response
        $reader = New-Object System.IO.StreamReader($responseBody.GetResponseStream())
        $reader.BaseStream.Position = 0
        $reader.DiscardBufferedData()
        $errorBody = $reader.ReadToEnd()
        $reader.Close()
        
        Write-Host "Response Code: $statusCode"
        Write-Host "Response Text: $errorBody"
        
        $errorReport = @{
            "Error" = @(
                @{
                    "Response Code" = $statusCode
                    "Response Body" = $errorBody
                }
            )
        }
        
        return @{data = $errorReport; success = $false}
    }
}

function Format-AsTable {
    param(
        [array]$BlackListNumbers
    )
    
    if ($BlackListNumbers.Count -eq 0) {
        Write-Host "No blacklist numbers found." -ForegroundColor Yellow
        return
    }
    
    # Create formatted table output
    $table = $BlackListNumbers | ForEach-Object {
        [PSCustomObject]@{
            'Number' = $_.number
            'Description' = if ($_.description) { $_.description } else { 'N/A' }
            'Block Calls' = if ($_.blockCalls) { 'Yes' } else { 'No' }
            'Block SMS' = if ($_.blockSms) { 'Yes' } else { 'No' }
            'ID' = $_.id
        }
    }
    
    return $table
}

function Show-Summary {
    param(
        [array]$BlackListNumbers
    )
    
    $totalCount = $BlackListNumbers.Count
    $blockCallsCount = ($BlackListNumbers | Where-Object { $_.blockCalls -eq $true }).Count
    $blockSmsCount = ($BlackListNumbers | Where-Object { $_.blockSms -eq $true }).Count
    
    Write-Host "`n=== Blacklist Summary ===" -ForegroundColor Cyan
    Write-Host "Total entries: $totalCount"
    Write-Host "Blocking calls: $blockCallsCount"
    Write-Host "Blocking SMS: $blockSmsCount"
    Write-Host "=========================`n" -ForegroundColor Cyan
    
    return @{
        TotalEntries = $totalCount
        BlockingCalls = $blockCallsCount
        BlockingSMS = $blockSmsCount
    }
}

function Export-ToFiles {
    param(
        [array]$BlackListNumbers,
        [string]$BasePath,
        [string]$Timestamp
    )
    
    # Create export directory if it doesn't exist
    $exportDir = "$BasePath\$Timestamp"
    if (-not (Test-Path -Path $exportDir)) {
        New-Item -ItemType Directory -Path $exportDir -Force | Out-Null
    }
    
    $summary = @{
        "ExportDate" = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
        "TotalEntries" = $BlackListNumbers.Count
        "FilesCreated" = @()
    }
    
    # 1. Export as JSON (raw data)
    $jsonPath = "$exportDir\blacklist_raw.json"
    $BlackListNumbers | ConvertTo-Json -Depth 10 | Out-File -FilePath $jsonPath -Encoding UTF8
    $summary.FilesCreated += "JSON: $jsonPath"
    
    # 2. Export as formatted table (TXT)
    $tableData = Format-AsTable -BlackListNumbers $BlackListNumbers
    if ($tableData) {
        $tablePath = "$exportDir\blacklist_table.txt"
        
        # Create a nicely formatted table for text file
        $tableOutput = @()
        $tableOutput += "=" * 80
        $tableOutput += "BLACKLIST NUMBERS - $((Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))"
        $tableOutput += "=" * 80
        $tableOutput += ""
        
        # Add summary
        $tableOutput += "SUMMARY:"
        $tableOutput += "Total entries: $($BlackListNumbers.Count)"
        $tableOutput += "Blocking calls: $(($BlackListNumbers | Where-Object { $_.blockCalls -eq $true }).Count)"
        $tableOutput += "Blocking SMS: $(($BlackListNumbers | Where-Object { $_.blockSms -eq $true }).Count)"
        $tableOutput += ""
        $tableOutput += "=" * 80
        $tableOutput += "DETAILED LIST:"
        $tableOutput += "=" * 80
        $tableOutput += ""
        
        # Format as table with fixed width columns
        $tableOutput += ("{0,-20} {1,-40} {2,-12} {3,-10} {4,-36}" -f "Number", "Description", "Block Calls", "Block SMS", "ID")
        $tableOutput += ("{0,-20} {1,-40} {2,-12} {3,-10} {4,-36}" -f "------", "-----------", "-----------", "---------", "--")
        
        foreach ($item in $tableData) {
            $desc = if ($item.Description.Length -gt 38) { $item.Description.Substring(0, 35) + "..." } else { $item.Description }
            $tableOutput += ("{0,-20} {1,-40} {2,-12} {3,-10} {4,-36}" -f $item.Number, $desc, $item.'Block Calls', $item.'Block SMS', $item.ID)
        }
        
        $tableOutput | Out-File -FilePath $tablePath -Encoding UTF8
        $summary.FilesCreated += "Table TXT: $tablePath"
    }
    
    # 3. Export as CSV
    if ($tableData) {
        $csvPath = "$exportDir\blacklist_export.csv"
        $tableData | Export-Csv -Path $csvPath -NoTypeInformation
        $summary.FilesCreated += "CSV: $csvPath"
    }
    
    # 4. Export as simple list (just numbers)
    $listPath = "$exportDir\blacklist_numbers_only.txt"
    $numbersOnly = $BlackListNumbers | ForEach-Object { $_.number }
    $numbersOutput = @()
    $numbersOutput += "Blacklist Phone Numbers"
    $numbersOutput += "Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
    $numbersOutput += "Total: $($numbersOnly.Count)"
    $numbersOutput += ""
    $numbersOutput += $numbersOnly
    $numbersOutput | Out-File -FilePath $listPath -Encoding UTF8
    $summary.FilesCreated += "Numbers only: $listPath"
    
    # 5. Export summary file
    $summaryPath = "$exportDir\export_summary.txt"
    $summaryOutput = @()
    $summaryOutput += "BLACKLIST EXPORT SUMMARY"
    $summaryOutput += "=" * 40
    $summaryOutput += "Export Date: $($summary.ExportDate)"
    $summaryOutput += "Total Entries: $($summary.TotalEntries)"
    $summaryOutput += ""
    $summaryOutput += "Files Created:"
    $summaryOutput += "-" * 40
    foreach ($file in $summary.FilesCreated) {
        $summaryOutput += "  • $file"
    }
    $summaryOutput += ""
    $summaryOutput += "Export Location: $exportDir"
    
    $summaryOutput | Out-File -FilePath $summaryPath -Encoding UTF8
    
    return @{
        ExportDirectory = $exportDir
        Files = $summary.FilesCreated
        Summary = $summary
    }
}

# Main execution
$token = Get-Token
$result = Get-BlackListNumbers -Token $token

if (-not $result.success) {
    Write-Host "Stopping process due to error:"
    Write-Host ($result.data | ConvertTo-Json -Depth 10)
    exit 1
}

$blackListNumbersList = $result.data.value

# Generate timestamp for export folder
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"

# Display on screen
Write-Host "`n=== Blacklist Numbers (Table View) ===" -ForegroundColor Green
$tableData = Format-AsTable -BlackListNumbers $blackListNumbersList
if ($tableData) {
    $tableData | Format-Table -AutoSize
}

# Show summary
$summary = Show-Summary -BlackListNumbers $blackListNumbersList

# Export to files
Write-Host "`n=== Exporting Data ===" -ForegroundColor Cyan
$exportResult = Export-ToFiles -BlackListNumbers $blackListNumbersList -BasePath $EXPORT_PATH -Timestamp $timestamp

Write-Host "`nExport completed successfully!" -ForegroundColor Green
Write-Host "Files saved to: $($exportResult.ExportDirectory)" -ForegroundColor Yellow
Write-Host "`nFiles created:" -ForegroundColor White
foreach ($file in $exportResult.Files) {
    Write-Host "  • $file" -ForegroundColor Gray
}

# Optional: Show location of export summary
Write-Host "`nExport summary: $($exportResult.ExportDirectory)\export_summary.txt" -ForegroundColor Cyan

# Optional: Ask if user wants to open export folder
$openFolder = Read-Host "`nOpen export folder? (Y/N)"
if ($openFolder -eq 'Y' -or $openFolder -eq 'y') {
    Invoke-Item $exportResult.ExportDirectory
}

Paulo
 

Forum statistics

Threads
111,953
Messages
589,914
Members
164,849
Latest member
BillyAkansel