Optimising the Polycom VVX for Lync/SfB

Polycom’s VVX family of phones is incredibly flexible, with a 23M Admin guide that spans 590 pages. I won’t say I’ve read *every* page of that, but I’ve spent weeks trawling the config options so as to assemble my “ideal” config for a VVX deployment operating as Lync peripherals.

Philosophy

My design philosophy for this config file was to make the phones as enterprise-grade as I could. I applied the mindset of a major corporate: the phones needed to be simple, with unnecessary functionality removed or suppressed. I wanted them to be secured, localised to the Australian environment, and to minimise the network traffic they would generate.

Setup

In this deployment I’ve condensed from my earlier three config files back to just two, with all of the config work now contained in a file I’ve called “customisations.cfg”. (In some of my recent articles you’ll see a ‘”shared.cfg” file had the Lync-specific values, which I’ve now moved to the top of “customisations.cfg”). When the phones boot and access the provisioning server they first look for a file called “<MAC Address>.cfg”. If that’s not found, their Plan B is the master config file: “000000000000.cfg”. The master config file provides global settings (like the location and filename for firmware, as well as the directories to place their own uploads) and also any other lower-level config files to look for. This master file then instructs the phone to download “customisations.cfg”, where it will find everything else it needs. At this point it’s assumed that you have the usual pre-requisites set: the DHCPUtil additions (for PIN-based sign-in to Lync), and DHCP-based auto-discovery of the provisioning server. (I covered the latter here).

Master Config file

The Master config file (000000000000.cfg) is pretty simple – note the “APP_FILE_PATH” is blank, because we’re using Lync as our source of firmware. (To use the FTP server for firmware upgrades this would be populated with “sip.ld”. You may still need to use this if your phones arrive running lower than UCS v5 firmware).
Master-Config

BTW if you’ve seen my earlier post “polycom-vvx-firmware-v5-now-with-btoe”, you might have noticed that it refers to a config file called “shared.cfg”. I’ve done away with that now and simply incorporated all of the relevant bits in “Customisations.cfg”.

Customisations.cfg

It’s into my customisations.cfg file that I’ve placed the rest of my config. The whole file can be downloaded at the bottom of this page, but here’s a detailed look at what’s in there, with some explanations, suggestions and alternatives. The file itself is full of comment text to help you find your way around.

Force Lync Mode

Here we’re forcing the base profile to be Lync, with device (firmware) updates to come from Lync, leaving only the customisations to come from the FTP/provisioning server.

<!-- Set the Base Profile to Lync mode, with firmware updates from Lync & user-popup enabled -->
<device device.set="1" device.baseProfile.set="1" />
<device device.baseProfile="Lync" device.prov.lyncDeviceUpdateEnabled="1" device.prov.lyncDeviceUpdateEnabled.set="1" />
<lync lync.deviceUpdate.userInactivityTimeout="900" lync.deviceUpdate.popUpSK.enabled="1" lync.deviceUpdate.serverPollInterval="28800" />

Phone Config Security

I mentioned at the top of this article that the phones are incredibly flexible. As well as configuring them with files from the provisioning server, you can login directly on the phone, or via a browser to its IP address, and tweak away. The only “problem” here is that these individualised settings override those from the central config file, so a determined power-user or mischief-maker finding the PDF online can potentially undo all your effort at a standardised, relatively idiot-proof configuration. So to maintain config purity we’ll need to lock them down. First we need to disable access to the in-built browser. Next we want to change the Admin password to something that you don’t find plastered all over the web. …and the pièce de résistance is to tell the phones to suck a new config file every day, just in case our Admin password has been compromised or we’ve made some other update. This won’t necessarily recover a device that might have gone rogue, but it helps limit the damage that could be done if the password’s compromised. The setting here tells all your phones to check the config daily on a random basis, between 1 and 5am. The two “tagSerialNo” entries just get the phone to add its MAC address to User-Agent headers and may help debugging or finding lost devices. The final value in this section (“checkSync.alwaysReboot”) enables a way for you to remotely force the phone to reboot. There’s more on how to do this on the Polycom support website.

<!-- Disable access to the phone's website, & move it off port 80 -->
<httpd httpd.enabled="0" httpd.cfg.port="8080" />

<!-- Passwords and Security -->
<sec sec.pwd.length.admin="3" sec.pwd.length.user="3" sec.tagSerialNo="1" />
<device device.auth.localAdminPassword="456" device.auth.localAdminPassword.set="1" />
<device device.prov.tagSerialNo="1" />

<!-- Automatic overnight config checks (improves device security) -->
<prov prov.polling.enabled="1" prov.polling.mode="random" prov.polling.period="86400" prov.polling.time="01:00" prov.polling.timeRandomEnd="05:00" />

<VoIpProt voIpProt.SIP.specialEvent.checkSync.alwaysReboot="1" />

Disable Phone Recording

The more advanced models in the range will permit the user to record calls. In many companies and parts of the world this is viewed as A Bad Thing. Whilst it’s disabled by default, I think it’s nice to specify it in the config file just to be sure.

<!-- We definitely don't want call recording on! -->
<feature feature.callRecording.enabled="0" />

Customising the Tones

I’ve previously documented the Aussie tones and cadences, so I won’t repeat them in this post. The content in the customisation file is copied verbatim from the earlier post. (Note the references under the XML in that post if you’re going to try customising these to another market).

NTP & Daylight Saving Time

Obviously outside of South-Eastern Australia the DST settings are going to be useless to you, but if you have daylight saving time it won’t take much to localise this. You can toggle the entire section off just by setting tcpIpApp.sntp.daylightSavings.enable=”0″. (I’ve covered the whole DST caper in more detail previously). If you have phones distributed across different time zones you’ll need to carefully manage this. With the values of tcpIpApp.sntp.address.overrideDHCP=”0″ and tcpIpApp.sntp.gmtOffset.overrideDHCP=”0″ the phone is going to use the NTP server offered by its local DHCP server & use the DHCP-supplied offset from GMT. Assuming you have a different DHCP server in each timezone and each points the phones to a localised VVX provisioning server with a state-specific version of “customisations.cfg” you should be sweet.

<!-- NTP & Daylight Saving : These values are applicable to NSW, ACT, SA, Broken Hill, VIC & TAS  -->
<tcpIpApp>
<tcpIpApp.sntp>
<tcpIpApp.sntp.address tcpIpApp.sntp.address.overrideDHCP="0"></tcpIpApp.sntp.address>
<tcpIpApp.sntp.daylightSavings tcpIpApp.sntp.daylightSavings.enable="1" tcpIpApp.sntp.daylightSavings.fixedDayEnable="0">
<tcpIpApp.sntp.daylightSavings.start tcpIpApp.sntp.daylightSavings.start.date="1" tcpIpApp.sntp.daylightSavings.start.dayOfWeek="1" tcpIpApp.sntp.daylightSavings.start.month="10" tcpIpApp.sntp.daylightSavings.start.time="2">
<tcpIpApp.sntp.daylightSavings.start.dayOfWeek tcpIpApp.sntp.daylightSavings.start.dayOfWeek.lastInMonth="0"></tcpIpApp.sntp.daylightSavings.start.dayOfWeek>
</tcpIpApp.sntp.daylightSavings.start>
<tcpIpApp.sntp.daylightSavings.stop tcpIpApp.sntp.daylightSavings.stop.date="1" tcpIpApp.sntp.daylightSavings.stop.dayOfWeek="1" tcpIpApp.sntp.daylightSavings.stop.month="4" tcpIpApp.sntp.daylightSavings.stop.time="3">
<tcpIpApp.sntp.daylightSavings.stop.dayOfWeek tcpIpApp.sntp.daylightSavings.stop.dayOfWeek.lastInMonth="0"></tcpIpApp.sntp.daylightSavings.stop.dayOfWeek>
</tcpIpApp.sntp.daylightSavings.stop>
</tcpIpApp.sntp.daylightSavings>
<tcpIpApp.sntp.gmtOffset tcpIpApp.sntp.gmtOffset.overrideDHCP="0" tcpIpApp.sntp.gmtOffset="36000" tcpIpApp.sntp.resyncPeriod="86400"></tcpIpApp.sntp.gmtOffset>
</tcpIpApp.sntp>
</tcpIpApp>

Power Saving

I was pleased to find that the phones have been designed with a clever power-saving feature: during “office” hours they’ll operate to one set of parameters, and outside of those hours they’ll operate by another. The intent here is that they’ll stay awake longer during the day and be quicker to drop to low power (with the screen off) at night. I fell out of love with this feature when I found that outside of office hours, the phone broadcasts this mode to the user, which I felt was irrelevant, and presented a very in-your-face reminder that they perhaps shouldn’t be at work… I couldn’t figure out how to suppress the message, so I opted to force the phones into office hours 24×7.
IMG_0840-highlight
With these settings, the phones will blank their display after 10 minutes, whilst the top-mounted red LED will flash every 3s to indicate it’s snoozing.

<!-- Power Saving : force "office hours" 24x7 & sleep after 10 minutes -->
<powerSaving powerSaving.enable="1" />
<powerSaving.officeHours powerSaving.officeHours.startHour.monday="0" powerSaving.officeHours.startHour.tuesday="0" powerSaving.officeHours.startHour.wednesday="0" powerSaving.officeHours.startHour.thursday="0" powerSaving.officeHours.startHour.friday="0" powerSaving.officeHours.startHour.saturday="0" powerSaving.officeHours.startHour.sunday="0" />
<powersaving.officeHours.duration powerSaving.officeHours.duration.monday="24" powerSaving.officeHours.duration.tuesday="24" powerSaving.officeHours.duration.wednesday="24" powerSaving.officeHours.duration.thursday="24" powerSaving.officeHours.duration.friday="24" powerSaving.officeHours.duration.saturday="24" powerSaving.officeHours.duration.sunday="24" />
<powerSaving.idleTimeout powerSaving.idleTimeout.officeHours="10" />

Background Image (Branding)

The phones can look a little plain without a background image, and many corporates will probably just prefer to have the company logo on-screen. If you’re wanting to experiment with this, there’s a “branding” PDF linked in the References at the bottom, and this extract is from it:
VVX-backgroundSizes
I experimented with a number of images here and found darker images with very little visual information in them worked the best. Dark imagery won’t clash with the bright buttons or clash with the banners of information at the top and bottom of the screen. (These captures are from the VVX410).

IMG_0852 IMG_0837

The left-hand sample image is of the radio mast that rises from the tourist town of Yulara, at Uluru (nee Ayers Rock) in the centre of the Australian outback, whilst the “busier” one on the right is Sydney Harbour. You may want to set aside some time to experiment with imagery that’s going to work well here. Or if in doubt, go vanilla and leave it out.

<!-- Specify background image and browser (Application) settings -->
<bg bg.color.VVX410.selection="2,1" bg.color.VVX500.selection="2,1" bg.color.bm.1.name="tower.jpg" />

Don’t forget to add extra phone references to the above if you’re running more than the two models I am.

Updated 26th August 2018: Read more about on-screen real-estate in my post Optimising Polycom VVX background images

Web-browser

If you click on the phone’s “Applications” button, it launches its internal browser and goes to a preconfigured website. As convenient as it might be in some environments, I wanted to kill it, but it wasn’t so easy. Leave the URL unconfigured and you see the left image below. Pointing it to the Front-End server’s “blank.html” presented the middle image, and so I cobbled together the page that results in the image on the right:

Config value left blank/null http://<FE-FQDN>/meet/blank.html http://<WebServer-FQDN>/vvx-home.html
IMG_0843 IMG_0850 IMG_0838
<mb mb.idleDisplay.home="" mb.main.home="http://#WebServer#/vvx-home.html" mb.main.autoBackKey="0" mb.main.statusbar="0" />

(Leave mb.idleDisplay.home blank and only change the value for mb.main.home).

Voicemail (Message Key)

Don’t forget to replace the value of “sip:#UM-ContactAddress#@contoso.com” below with the SIP address of your Exchange UM contact object. (You’ll find that from C:\Program Files\Common Files\Microsoft Lync Server 2013\Support\OcsUmUtil.exe on your Front-End).

<!-- Set the "Messages" key to call Voicemail -->
<msg>
<msg msg.bypassInstantMessage="1" />
<msg.mwi.1 msg.mwi.1.callBackMode="contact" msg.mwi.1.callBack="sip:#UM-ContactAddress#@contoso.com;opaque=app:voicemail" />
</msg>

Exchange (Calendar)

I opted to omit this functionality, partly due to the privacy concerns (especially if you’re not going with the Phone Locking, described later below), but also due to the need to enter the user’s credentials in the phone.

Dial Plan

We don’t need to build a Dial Plan into the phone, as it’s going to abide by the one granted to the user in Lync. I have however used this as an opportunity to override the default “911” settings. In the US, delete this section from the config file, and in other countries, simply substitute your local emergency numbers.

<!-- Remove the default US E.911 values (obviously, *delete* this section for US locations!!) -->
<dialplan dialplan.1.e911dialmask="112;106" dialplan.1.e911dialstring="000" dialplan.routing.emergency.1.value="000" />
<!-- Just dial any unknown number strings after 4s. Let Lync handle it... -->
<dialplan dialplan.impossibleMatchHandling="3" dialplan.impossibleMatchHandling.timeOut="4" />

A special mention here needs to go to Jonathan McKinney for finding a bug in the way the current 5.0.1 firmware handles Lync’s normalisation rules. If you’re not already familiar with this, here’s his post.

Telephony Features

Add an audible reminder of a held call. Start after 30s on hold and remind every 30s.

<!-- Enable Audible Reminder of Held Call feature -->
<call call.hold.localReminder.enabled="1" call.hold.localReminder.period="30" call.hold.localReminder.startDelay="30" call.transferOnConferenceEnd="1" call.BlindTransferSpecialInterop="1" />

Leave call.transferOnConferenceEnd=”1″. When established by the phone, a three-party conference call is created locally (not via the Lync MCU). If the initiator of the conference leaves the calls, this setting transfers the other two parties together so they can continue the conversation. By default the phone offers a “New Call” softkey on the very LH button. I felt this was redundant, as pressing the line key does the same. Here I’ve removed it and replaced it with the more practical Redial key. I found when I was testing announced transfers that Split didn’t work the way I expected. You could “split” an announced transfer and toggle between both parties, but you could then never complete the transfer. I solved that problem by removing the key! “call.BlindTransferSpecialInterop” is new in 5.1.0.15902 and addresses a few problems getting Blind Transfers to work under Lync.

<!-- Hide Split and NewCall keys -->
<softkey softkey.feature.split="0" softkey.feature.newcall="0" softkey.feature.simplifiedSignIn="1" />
<!-- Add a redial button -->
<softkey softkey.1.enable="1" softkey.1.label="Redial" softkey.1.use.idle="1" softkey.1.action="$FRedial$" />

Miscellaneous Bits

Enable BToE &  Suppress Warnings

<!-- Misc : a Warning Level of "1" suppresses the display of nuisance warnings (like the downstream PC is disconnected) -->
<up up.warningLevel="1" up.oneTouchVoiceMail="1" />

<feature feature.btoe.enabled="1" feature.enhancedFeatureKeys.enabled="1" />

(The values here for oneTouchVoiceMail and enhancedFeatureKeys are required for some of the other settings to work correctly).

Persist Volume Settings Between Calls

<!-- Volume persist settings: a "1" will retain the volume settings after each call -->
<voice voice.volume.persist.handset="1" voice.volume.persist.headset="1" voice.volume.persist.handsfree="1" />

(If you’re using Bluetooth or USB headsets there are some more related settings under “voice.volume.persist” in the Admin guide).

Time and Date Display

<!-- "0" gives a 12-hour clock, "1" is 24. The admin guide has more clock & time/date display options -->
<lcl lcl.datetime.time.24HourClock="1" />

Line Key Labelling

In 5.0.2 Polycom toggled the default line key label from the number to the name – but just in case you liked it the way it was, they gave you the option to set it back. Here’s what it looks like depending upon your Line URI format and the value set in the config file:

“0” “1” & LineURI “+61270001236” “1” & LineURI “+61…;ext=1236”
label-name label-number label-ext
<!-- 1 = Label the Line key with the number, 0 = Show the name -->
<reg reg.1.useTelUriAsLineLabel="0" />

(The config file attached below has this value set at 0, so the name will show if you run it as-is).

Audio Codec Priority

Here and in other non-US markets, you’ll probably want to reorder the codec priority so A-law is chosen ahead of mu-Law.

<!-- Swap the preference for A/u-law. (*delete* this section for US locations!!) -->
<voice voice.codecPref.G711_A="6" voice.codecPref.G711_Mu="7" />

Disable the Picture Frame Feature

Party pooper? Maybe – but plenty of corporates don’t want memory sticks being plugged into network-connected devices. This won’t stop the users doing it, but it certainly removes an incentive to do it.

<!-- "0" disables the picture frame in the VVX500 and 1500 (otherwise on by default) -->
<feature feature.pictureFrame.enabled="0" />

Minimise the Phone’s Logging

Set the lowest logging  level to be 6 (critical errors only). If you encounter bugs with the phones in the field you might choose to lower this). Only append to a log file up to 128k. From there, delete and start anew.

<!-- Minimise logging uploads -->
<log log.render.level="6" log.render.file.upload.append.sizeLimit="128" />

Phone Lock

You may or may not want to have the phones auto-lock, or to give the user the ability to lock the phone. Here’s one point where the Aries family is superior, as its lock will follow the state of the connected PC. Here, we’re not quite so lucky. I’ve opted to give the user a Lock button, and the phone will only allow calls to up to five pre-configured numbers when it’s locked. (I like that the phone automatically permits a call to Triple-0 without me needing to add that).
IMG_0845
The downside of this is that the user needs to know their User-level password, and after they change and forget it, the tightened security settings earlier are going to make it harder for the Admins to reset this (because we can no longer login to the phone’s browser remotely). You’ll need to create a temporary copy of customisations.cfg as <MAC-Address>.cfg file with <device device.auth.localUserPassword=”123″ device.auth.localUserPassword.set=”1” /> and then get the user to reset the phone or force it by toggling PoE or sending a reset message to it. Needless to say, you might find this not worth the pain, or you might instead choose to re-enable the phone’s web interface.

<!-- Give the user the ability to lock their phone. (Auto-lock is disabled here, but an option) -->
<phoneLock phoneLock.enabled="1" phoneLock.authorized.1.description="Taxi" phoneLock.authorized.1.value="+6112345678" phoneLock.authorized.2.description="Pizza" phoneLock.authorized.2.value="+61270001223"/>
<softkey softkey.2.enable="1" softkey.2.label="Lock" softkey.2.use.idle="1" softkey.2.action="$FLockPhone$" softkey.2.precede="0" />

QoS

Tweak these as required.

<!-- Tweak the QoS values & RTP media port as required -->
<qos qos.ethernet.callControl.user_priority="5" qos.ethernet.other.user_priority="2" qos.ethernet.rtp.user_priority="2" qos.ethernet.rtp.video.user_priority="5" qos.ip.callControl.dscp="46" qos.ip.rtp.dscp="46" qos.ip.rtp.video.dscp="34" />

<tcpIpApp tcpIpApp.port.rtp.mediaPortRangeStart="5350" />

 


Debugging & Faultfinding

Every time you load a new config into a phone, you should check that the syntax is correct. Hit Settings / 4.Status / 1.Platform / 3.Config and the screen there will tell you if there are any errors in the various config files. In this example I have 1 bad entry out of 162 – but it offers me no more clues. The only way I’ve been able to weed these out has been by using a binary search: delete half the file, save, reboot, re-test. If the error’s gone, it was in the chunk you’ve deleted. Reinstate the chunk, then delete half of it, and repeat.
IMG_0844-error

My Wish-List

There were a few things I wanted to get the phones to do but couldn’t figure out how. It might be that someone else has already been able to address this, or it might be something supported in a future firmware update.

  • When the phone is signed-out, move the sign-in soft-key to the main screen
  • Disable/remove the Applications key

Download

The config file now lives in my VVX Github Repo.

References / Credits

  1. Polycom UC Software 5.8.0 Administrators’ Guide
  2. Using Polycom® VVX® Business Media Phones with Microsoft® Lync™ Server 2013
  3. Branding Your Polycom® Phone
  4. Customizing the Display Background on Polycom®  VVX® Business Media Phones
  5. Suppressing soft-keys on the Applications page (pp 91-93)
  6. Using Enhanced Feature Keys and Configurable Soft Keys on Polycom® Phones
  7. How To Disable Default Softkeys And “Do Not Disturb” Feature
  8. Optimising Polycom VVX background images
  9. A VVX Courtesy Phone

Thanks to Polycom Australia for the loan of a VVX410 and some config guidance, and as always to Rocky for the glossies.

Change Log

26th August 2018: Updated doco links and redirected to the Github repo.
22nd May 2015: Referenced the consolidation of ‘shared.cfg’ into ‘customisations.cfg’ in Master Config file & updated the admin guide link to the 5.3.0 version
7th Oct 2014: Updated admin guide link to the 5.2.0 version
18th September 2014: Updated Admin Guide link in References. Added Line Key Labelling (to post & config file)
21 May2014: Added “call.BlindTransferSpecialInterop” under Telephony Features.

 
– G.

19 Comments

  1. Hi Greg

    Great article has made my life a lot easier, however, I have one issue when I use your customisations file. When using the Lync server to update the VVX firmware I no longer get the pop up “Device Update available. Press reboot to Update” Under status -> Diagnostics -> Lync Device Update it shows that the new firmware is available.

    I have seen the pop up when using no config file, any ideas?

    Cheers Wayne

  2. Hello,

    We are using some Polycom VVX 500 in test under a Lync Hybrid architecture. Everything is working well but we encounter one main issue. On the Phone, used for sure per OnPremise user, we don’t see the status of OnLine users. Do you ever meet this case in your different tests?

    Crodially

  3. Hi Greig,

    Rrunning 5.2.2 firmware on vvx410 and the configuration settings for setting the admin password are not applying the new password I am setting so we keep getting the red BANG! waring of a default password which it is, any thoughts?
    thanks

  4. Any hints to disable softbuttons “callforwarding” (since V5.4.x) , because the use of that, will activate the default Voicemail – and users without Voicemail (well no enterprise-users) will hang up the call in 20 seconds (4 times of ringing).
    In 5.3.1 it was still possible to deactivate it.
    microsoft want`s to push you to buy enterprise-cals, but i would like to disable this feature in Polycom.
    Also no UC-Enables/Voicemail-enabled user will get errors on Polycom VVX600 since 5.3.1.

    • Hey Markus, I hadn’t noticed that – thanks for pointing it out. It looks like we’re stuck with the Forward key (at least for the time being?). I notice the 5.4.1 doco still gives us the option, so I’m guessing this will be removed or the functionality restored in a future update.

  5. Great article. I’m pretty new in the SfB/VVX world (coming from Cisco CUCM) and this site managed to give me a lot of useful information on how to setup the Polycom part.

    I found, that adding the following header at the start of the customisations.cfg file helps a lot when using XML Notepad, as the syntax is automatically checked by XML notepad:

    This way, no config errors happen anymore and XML notepad even shows the supported values for the fields. Very handy.

    One thing, I’m trying to solve, for which I haven’t found a solution yet: Conference phones (e.g. SoundStation IP 5000) go to “Away” status automatically after a certain time. While this doesn’t prevent them from functioning correctly, it’s not nice. If you can think of a hint on how to prevent this, while not disabling the auto-busy during phone calls, that would be very cool.

  6. Ooops. My XML was eaten by the comment system in my last post. I replaced the XML-brackets by []-Brackets – hope this helps…

    [?xml version=”1.0″ encoding=”utf-8″ standalone=”yes”?]
    [polycomConfig xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”Config/polycomConfig.xsd”]

  7. Great blog post here Greg. Thanks a lot. I stumbled across your blog while ramping up my VVX deployment skills. I like what you’ve done here.

    I really wish that Applications button could be removed! I was reviewing the Admin guide for UCS 5.4.0, maybe it can be removed now? Looking at Page 350 of the guide.

    Homescreen Parameters

    Parameter Permitted Values Default

    homeScreen.application.enable 0 or 1 1

    Enable or disable display of the Applications icon on the phone Home screen.

    I am trying to get this to work in my configuration file but it appears to not be working. Maybe my formatting is wrong?

    Any thoughts?
    Thanks
    -Matt

  8. Hi Greig,

    Your blog helped me a lot for making VVX phones deployment through provisioning server.
    However, I am looking for an option to disable the favourite contacts on the VVX 410 homescreen and changing the 911 emergency number to local emergency number through .cfg file.
    I was not able to figure out which values needs to be add or change in .cfg files.

    It would be great if you help me on this.

    Thanks in advance.

Leave a Reply

Your email address will not be published.

... and please just confirm for me that you're not a bot first: Time limit is exhausted. Please reload the CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.