I recently noticed that this howto was gone due to the "Linux Know How" section being removed some time ago. This one is actually useful though, thus I re-add it as a blog post now.
Virtual users (users with no real account on the system) can be easily set up for vsftpd. We will use PAM's pam_userdb module to authenticate the virtual users.
We need one real user for this to work. I simply used the ftp user, as this one is normally already there and has no shell login anyway. Of course, you can use or create any user you want for this. Set the home directory of this user to the root of the FTP directories we want to serve. E. g.:
usermod -d /srv/ftp ftp
Here we have the relevant part of the /etc/vsftpd/vsftpd.conf I use:
Of course, you are free to add other options, like logging, umask, etc.
The config says it uses vsftpd.virtual to authenticate the virtual users. So let's also create the respective PAM config file /etc/pam.d/vsftpd.virtual:
Notice the reference to /etc/vsftpd/users, not /etc/vsftpd/users.db (which will be the actual filename used). We append crypt=crypt to indicate we want to use crypted passwords (nobody wants to store clear text passwords, does anybody?).
Finally, we need to add one or more virtual users to the userdb. I wrote a script called userdbadm to do this in an easy way. Of course, you can create the database in whatever way you want to. When using userdbadm, it's something like:
userdbadm /etc/vsftpd/users.db add virtual_user
Notice the .db here! You will be prompted for a password. The user and the salted crypted password will be stored in the database.
Finally, the virtual user needs a home directory which will be served by vsftpd when the user logs in. It has to be owned by the real user vsftpd uses. For the above example, we simply do
My oldest still maintained project b8, the statistical PHP spam filter, got an overall code refactoring and modernization. After all the years, this really was necessary!
The code has been modernized a lot since the last release. Most notably, namespaces have been added. So, you have to instantiate b8 e. g. like this now:
$b8 = new b8\b8(...);
To use the constants, please also add the namespace, e. g. b8\b8::HAM.
Due to the namespace introduction, the default degenerator and lexer can't be called default anymore. The name is now standard (e. g. b8\lexer\standard).
Storage backend approach change
The storage backends now leave the connection to a database to the user (where it belongs). The Berkeley DB (DBA) storage backend remains the reference one. The other remaining one shows how to store b8's wordlist in a MySQL table, more as an example how to implement a proper storage backend. The base storage class now has all needed functions added as abstract definitions, so that everybody can easily implement their needed backend. Also, some function names have been changed to more meaningful ones.
The DBA backend now simply wants to have a working DBA resource as the only parameter. So if you use this, you would do e. g.:
The (example) MySQL backend takes a mysqli object and a table name as config keys. Simply look at the backends themselves to see the changes.
If you implemented your own backend, you will have to update it. But this should be quite straightforward.
Please notice the newly added start_transaction() function. Actually, with MySQL's MyISAM engine that was the default back then, transactions didn't even exist (man, this project is actually quite old ;-)!
Additionally, the PostgreSQL backend and the original MySQL backend (using the long-deprecated mysql functions, not the mysqli ones) have been removed.
New default configuration
The default configuration of the lexer and the degenerator has also been changed.
The degenerator now uses multibyte operations by default. This needs PHP's mbstring module. If you don't have it, set multibyte to false in the config array.
Speaking of the lexer, the legacy HTML extractor has been removed, alongside with it's old_get_html config option.
I wanted to calculate the difference between two QDates. Not only the days, but also the years, months, and weeks (for use in KPhotoAlbum).
I ended up using the following algorithm:
[Update 2024-02-07]: In some cases, the days weren't calculated correctly, becase a day surpassing the days in that specific month has been used, resulting in an invalid QDate. This is now fixed.
[Update 2024-03-05]: Now, we also calculate timespans correctly if the date we refer to is a February 29.
struct DateDifference
{
int years;
int months;
int days;
booloperator==(const DateDifference &other) const
{
returnthis->years == other.years
&&this->months == other.months
&&this->days == other.days;
}
booloperator!=(const DateDifference &other) const
{
returnthis->years != other.years
||this->months != other.months
||this->days != other.days;
}
};
DateDifference dateDifference(const QDate &date, const QDate &reference)
{
if (date > reference) {
return dateDifference(reference, date);
}
int dateDay = date.day();
if (date.month() ==2&& dateDay ==29&&! QDate::isLeapYear(reference.year())) {
// If we calculate the timespan to a February 29 for a non-leap year, we use February 28// instead (the last day in February). This will also make birthdays for people born on// February 29 being calculated correctly (February 28, the last day in February, for// non-leap years)
dateDay =28;
}
int years = reference.year() - date.year();
int months = reference.month() - date.month();
if (reference.month() < date.month()
|| ((reference.month() == date.month()) && (reference.day() < dateDay))) {
years--;
months +=12;
}
if (reference.day() < dateDay) {
months--;
}
int remainderMonth = reference.month() - (reference.day() < dateDay);
int remainderYear = reference.year();
if (remainderMonth ==0) {
remainderMonth =12;
remainderYear--;
}
constauto daysOfRemainderMonth = QDate(remainderYear, remainderMonth, 1).daysInMonth();
constauto remainderDay = dateDay > daysOfRemainderMonth ? daysOfRemainderMonth : dateDay;
int days = QDate(remainderYear, remainderMonth, remainderDay).daysTo(reference);
return { years, months, days };
}
Perhaps, this will help somebody.
I also filed a Feature request about adding such a function to Qt directly. Hopefully, it will be added in Qt 5.14 (to the new QCalendar class) :-)
Back from our vacation in the Bavarian Forest, I noticed that my long-serving and really reliable iBlue 747A+ GPS logger apparently stopped working: it delivered tracks with a date around year 2000. Investigating this further, I found that the time appeared to be okay, but the date was completely wrong.
Fortunately, the device isn't broken, it was just hit by the GPS Week Number Rollover, that took place on 2019-04-07. This really sucks! But blessedly, I can continue using the logger. One can fix the wrong date by adding 172,032 hours to the date (that is: 1024 weeks times 7 days times 24 hours).
This can be done via gpsbabel in the following way (assuming we have a GPX encoded GPS data file):
Hopefully, Europe's Galileo GNSS won't suffer from such shortcomings and my next "GPS" logger will use this one … and the good ole' one will continue working a few years until Galileo is finished, working and a lot of devices support it ;-)