Howto: Setup and emulate a Windows NT Domain on Linux and make Windows 2000/XP log on

Howto: Setup and emulate a Windows NT Domain on Linux and make Windows 2000/XP log on
===========================================================================

HI all,
I'm back with another Tutorial in the Linux series. This time we're going to with an issue that is very
common in everyday networking and is implemented almost everywhere in some form or the other. The primary issue
here is to make two DIFFERENT Operating Systems talk to each other over the network and synchonize and share files
without letting off any hint about the complex protocols involved in between. Windows 2000/XP are used by most home
users as standalone workstation. Those who have cared to venture into Windows Networking and tried out the Host to
Domain logon model would have an idea where I'm getting at. Normally, a windows workstation would only log onto a
domain that is being served by a server called Primary Domain Controlled or PDC in Windows
Networking terms. Following this model if we have a machine running a Windows Server behaving as the PDC
and several Windows Workstations which allow individual users to log onto this server - what we get is a
massive sharing of resources by all these workstations at a One Pass Authentication, i.e. Whatever shared resources
are attached to the server (printers, tape drives - any kind of peripherals) - are made available to EACH workstation as soon as the user logs into the domain. One
DOES NOT need to enter a separate set of login credentials (username/password) to access each of these shared resources
as it happens when you setup a simple bus network using multiple windows workstations.

Fortunately for us, we have something called SAMBA on Linux, that is capable of emulating Windows Domains and can
let users running Windows log onto this emulated domain using their login credentials for Linux. In turn, they reap the
great benefits of a Linux Server (security, high uptime & stability etc.) while being able to work on all their favourite
applications on Windows. The home drives that are created on Linux for each user (usually in the /home/ folder
are directly mapped on as an extra Physical Drive Letter (say, H:, I:, J: ... whatever you choose it to be) on your
Windows machine - and whatever you save into this drive gets automatically transferred to your home drive on the Linux
Server. The origin of the name SAMBA is from SMB which stands for Server Message Blocks - a protocol used to share
files between different Operating Systems with relative transparency. Find out more about SAMBA @ http://www.samba.org

My experimental platform is exactly the same as what I'd used for setting up the Domain Name Server on Linux. Today
I successfully managed to setup this Windows Domain on Linux and here I am sharing a little more of my adventures on taming
the "Linux Beast".

However, unlike the DNS configuration - this was a pleasant breeze. The process is very simple and surprisingly can be
accomplished in a very few steps. Besides, the only configuration file that we have to edit is smb.conf that resides
in the /etc/samba/ directory.

Requirements (for this experiment)
===================================
a. A Server running on Linux - that has the smbd or Samba Daemon up and running
b. A Windows XP/2000 Pro Workstation - physically connected to the server
===================================
If you are unsure about the smbd service, check with service --status-all | grep smb - this shoudld return you
a message like smbd (pid 5831) is running.... If not, you can fire up the service by simply typing smbd -D.


Step 1 - Editing the /etc/samba/smb.conf file

This is the one and only file used for configuring the Samba Daemon and there are only a few parameters that you have to
edit. Open this file in your favourite editor.

Right near the beginning you'll find a section called [workgroup]
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# workgroup = NT-Domain-Name or Workgroup-Name
workgroup = asterix
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The default smb.conf will contain some other name as the name of the workgroup - I set it to "asterix" for my
system. Feel free to change it to whatever you like - but keep it less than 15 characters. It can contain Alphabetic
characters, Numbers and Underscores ONLY.

Scroll down a little below till you find this line:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Security mode. Most people will want user level security. See
# security_level.txt for details.
security = user
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The line "security = user" might be commented out with a "#". If so, just remove the "#" at the beginning.

Go a little further down again and find the line:

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# You may wish to use password encryption. Please read
# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
# Do not enable this option unless you have read those documents
encrypt passwords = yes
smb passwd file = /etc/samba/smbpasswd
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Once, again, the
encrypt passwords = yes
smb passwd file = /etc/samba/smbpasswd
lines are likely to be commented. Remove the comments. You can choose an alternate location for the
samba password file, but leaving it where it is wont harm in any way.


A little further down you'll meet another large block of commented out statements.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Browser Control Options:
# set local master to no if you don't want Samba to become a master
# browser on your network. Otherwise the normal election rules apply
local master = yes

# OS Level determines the precedence of this server in master browser
# elections. The default value should be reasonable
; os level = 65

# Domain Master specifies Samba to be the Domain Master Browser. This
# allows Samba to collate browse lists between subnets. Don't use this
# if you already have a Windows NT domain controller doing this job
domain master = yes

# Preferred Master causes Samba to force a local browser election on startup
# and gives it a slightly higher chance of winning the election
preferred master = yes

# Enable this if you want Samba to be a domain logon server for
# Windows95 workstations.
domain logons = yes
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Uncomment the line "local master = yes", "domain master = yes", "preferred master = yes" and "domain logons = yes".
If any of them equate to "no", set it to "yes". The "os level = 65" is usually set to a much lower value, but setting it
to 65 gives a big performance boost according to man pages.

Right in the next block, you'll find these statements:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# if you enable domain logons then you may want a per-machine or
# per user logon script
# run a specific logon batch file per workstation (machine)
; logon script = %m.bat
# run a specific logon batch file per username
logon script = %U.bat
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Both the "logon script = %m.bat" and "logon script = %U.bat" and commented out. I am using a logon script on per user
basis - so that's the one I uncommented. A word about logon scripts here. This logon script will reside on the Linux Server
itself, but it is actually a MS-DOS BATCH FILE. It's not directly run by Linux, but dished out to the Windows workstation
once the login credentials are settled. This logon script may contain any number of commands, ranging from commands to map
your Linux HOME DRIVE to a logical windows drive and/or synchronizing your workstations CLOCK with the Server's Clock.
We'll come to this later on towards the end of the tutorial. If you uncomment the "logon script = %m.bat" line, then your
logon script's name has to be WindowsNameOfYourWorkStation.bat. If you are using per-user basis like me, then you'll
have to create a copy of this script with the name of every user that intends to log onto your domain. As you can guess,
the %m and %U variables expand to take on the machine name and user name respectively. DONOT, under any
circumstances uncomment BOTH. That could lead to a lot of confusion for the Domain Controller. More later.

Towards the bottom end of the file you are going to find a large section dedicated to mapping different shares between
Windows and Linux. Find the following section named "netlogon":
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Un-comment the following and create the netlogon directory for Domain Logons
[netlogon]
comment = Windows Network Logon Service
path = /home/netlogon
; guest ok = yes
writable = no
public = no
; share modes = no
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

In my default .conf file, the comment was different and I changed it to the "Windows Network...." - you can modify
it to whatever you feel like. Next the line "path = /home/netlogon" - uncomment this and set the path to point to whatever
directory you want to keep your logon scripts in. Set "writable" and "public" to "no". Comment out "guest ok = yes"
and "share modes = no".

THAT'S IT. Save the file and quit.


Step 2 - Setting up Machine Account & User Accounts in SAMBA

All the Windows machines that will log onto the Linux Domain are required to have an entry corresponding to their Windows
names, in the samba database. The machine names as well as the user names are to be added to a group called "smbuser"
which doesn't exist on its own. So first create this group:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> groupadd smbuser
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Next, we create an entry with the name of the Workstation that is going to hook onto this domain controller. Find out
the Windows name of your system (Desktop > My Computer > Right-Click > Properties > Network Identification TAB > Properties).
In the dialog box that comes up you'll find a field called Computer Name[b]. That is the name of your machine. In my case
the windows name of my workstation IS [b]"WorkStation". So I used that here. Replace it with yours.
This name (it was "Workstation" in my case) added with a "$" sign at its back is going to be your machine name in
samba. So "Workstation" becomes "workstation$". Next use the following command to add this to Samba.
Note: The name that you find on your Windows system might contain MIXED CHARACTER CASING - but for Linux, convert the whole
name to LOWERCASE and then add the "$" sign.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> useradd -g smbuser -d /dev/null -s /bin/false workstation$
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Next, add this windows client to the samba password databse.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> smbpasswd -a -m workstation
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Note, that this time we DO NOT INCLUDE the "$" at the end of the computer name. The option -a tells samba to
add the client name and option -m specifies that this name is the name of a computer and NOT a user.


Next, what we are going to do is CREATE user accounts in Samba, which will be used to login from the Windows machines.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> useradd -g smbuser -d /dev/null -s /bin/false microscopicearthling
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
One word here - notice we are alloting a null directory and null shell to the users and the machine name - since
these users won't need shell access & can login directly from windows.

If you already have some users setup in your Linux Server, you can skip this step and add the user directly to the samba
password database. If that case the samba user will inherit the home folder that had been created while creating the user
account. Say, I have an existing user acount called "someone". I'll use the following command to add him to the samba db.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> smbpasswd -a someone
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Notice that I've removed the "-m" option, since this is an actual USER that we are adding. For any other user, replace the
"someone" with the corresponding username. You can change the PASSWORD that the user will use, by using:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> smbpasswd someone
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
But make sure that the user has been added to the samba db through the step right before this - or else "smbpasswd" will
spit out some error message like:
Failed to find entry for user someone.
Failed to modify password entry for user someone

Another important point: the user you are adding to the samba db - has to exist as a valid user of the Linux Server, i.e. the user has to have an active account on the server created with the command "useradd". Only then, he can be added to the samba db as a remote logon user.

Next, add the user "root" into the smbpasswd db the same way:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> smbpasswd -a root
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


Step 3 - Configure the netlogon.bat - LOGIN SCRIPT file

Recall that while we were editing the smb.conf file, we came across a line: "path = /home/netlogon" towards the end
of the file. Switch over to this directory now. The directory wouldn't be created automatically, so you need to change to
/home and create one called netlogon in it. Now enter this directory and fire up your editor. Create a file
called "netlogon.bat" that will server as a template for all users. Whenever you add a new user to the samba db, you
have to make a copy of this file as that username.bat So for a new user, "someonelse" we'll simply copy over
netlogon.bat as someonelse.bat.

The contents of the batch file will be as follows:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
net use H: /HOME
NET TIME \\getafix /SET /YES
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

The first entry maps your Linux Home folder as a DRIVE named H:\ in Windows. So whatever you save in drive H: gets
saved directly to your home folder on the Linux Server - and the files/folders - all acquire the strong security settings
that Linux offers. Thus no one else should be able to view your files - unless you set their attributes such that they get
shared with others in your group or domain.
The second line, sets the TIME of your Workstation by syncing it with the time of the server. The \\getafix is
the hostname of my server. Replace it with whatever your Linux server hostname is.
Save the file and quit.


Step 4 - Restart smbd

The Samba daemon needs to be restarted so as to load the new configuration options. Simple step, just do:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
shell> smbd -SIGHUP
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


Step 5 - FINAL Step: Make your Windows Workstation join the Linux Domain

Once again do Desktop > My Computer > Right-Click > Properties > Network Identification TAB > Properties. The
lower part of the dialog box should comain two fields with radio buttons namely, Domain and Workgroup. Normally,
you'd see some random entry in the workgroup field - usually from the settings that you had specified during windows installation.
Click the radio button beside the DOMAIN and enter the name of the domain that you'd specified in your smb.conf file right
at the beginning using the clause "workgroup = asterix". In my case, I entered asterix as the domain name here and clicked
OK.

There will be a short delay, after which you'll be asked to enter a pair of login credentials that has authority to join
the samba domain. Use your root/password combination. After another short wait, you'll be informed that your workstation has
successfully joined the domain and that you should restart your computer for the changes to take effect.

Upon reboot, you'll see a completely different kind of splash screen that you've never seen before in standalone mode. It'll
tell you to press Ctrl-Alt-Del to login and thats what you should do. Next, you'll be presented the standard login
screen. Click on the Options and you'll see one more dropdown list titled "Log onto:" - click on that and you'll
be presented with TWO options. One is the name of your Windows machine - which will be selected by default. If you use this -
you'll log on locally - as you'd do on a standalone sytem. The OTHER one is the name of the Linux Domain that you just joined.
Select that and enter the username/password that you had created for yourself or "someone" in the samba password db.

That's it - you should log into a windows normally - but beware you wouldn't find most of the icons on your desktop that
you normally have when you log on locally as an administrator. You'll be presented with a bare minimum set of icons, determined by the windows access rights that you've specified for your system. Most of the common applications will be there in the Start Menu though. To log back in locally, just log out and switch the "log onto:" option to your local machine name.

When you click on My Computer you should see another drive called H: which as I said before is mapped onto your
home folder on Linux Server.
===========================================

WARNING: I had to come back and add this part - I believe it's very necessary to know what you are heading for when you setup a login process like this.
Windows 2000 and XP have something called "ROAMING PROFILES" which basically means that whatever you save on your Desktop - all your files, icons & registry and windows settings propagate to the Linux server when you log out and gets saved in your home folder. WHen you log back in these setting migrate back to your local windows system and take effect - creating the exact desktop state you'd left it in. This ensures all the personal preferences of every user using these systems remain intact. While the feature sounds good - it's a HUGE DRAWBACK from networking perspective - as it can create immense bottlenecks. These profiles are not small in size by any means - each profile is at least 4-5MB in size. When the network is small and consists no more than 10 computers - this is pretty all right to have enabled. But when you consider the a network of nearly 150 computers (like my school network) - with over 500 users logging in and out several times a day - you can imagine the amount of traffic this generates - just by downloading the profile when you log in and uploading it back when you log out. This alone can bring the whole network down in a matter of days.
SOLUTION: Turn off the Roaming Profiles in Win2k/XP on your windows workstatoin when you use this model. The performance gain achieved is thousand folds better than clogging the whole network just trying to save your icon settings. You can do so by opening the Start Menu > Run > and typing gpedit.msc in there - in both Win2k and XP. This will bring up the Group Policy Editor. Follow this route: Local Computer Policy > Computer Configuratoin > Administrative Templates > System > Logon. This brings you to a panel on the right where you can turn off the roaming profile. In XP it is very easy. There will be an option called Only allow local user profiles and Prevent Roaming Profile Change from Propagating to the Server. Enable these two and your job is done. For Windows 2000 - you have to look around in the same panel and have to enable/disable a combination of options to disable to roaming profile as a whole. More on Win2K later.


Have fun....and all the best smile.gif


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
LINUX: What Windows will NEVER BE wink.gif
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

PHP Script to measure PHP execution time

Here I’ll show you how to we can show users how long it takes for php to execute that particular page.

It’s a very simple script and is accurate to 0.000000000000001 seconds.


Firstly place the following code at the very top of your page (before the tag.

< ?php
$time = microtime();
$time = explode(' ', $time);
$time = $time[1] + $time[0];
$begintime = $time;
?>

Next add the following code at the very bottom of the page. You can change the last line to whatever you would like your viewers to see.

< ?php
$time = microtime();
$time = explode(" ", $time);
$time = $time[1] + $time[0];
$endtime = $time;
$totaltime = ($endtime - $begintime);
echo 'PHP parsed this page in ' .$totaltime. ' seconds.';
?>

How to Protect PHP Code

A classic method to protect your PHP code like Fort Knox is to organize your PHP code and make sure hackers are not passing harmful GET and POST parameters to your scripts. If you have variables that are not initialized, they can be open to outside manipulation. A great example is a Boolean value that is initialized to an untrue value. Implementing a variable screening procedure at the commencement of every code execution is the solution to this security problem. Typically this is done using a switch statement. Setting this up in the most efficient way can be done in just a few quick steps.

Protect PHP Code Step One:
You need to put all of your PHP code in a separate folder! Do not leave it sitting in the same folder as the index.php file; this is the most important step to protect your PHP code. All PHP files in the root directory are easy targets for hackers to access. The folder name we will use in this example is inc, for include.

Protect PHP Code Two:
After we have all of our PHP code in the include folder we will need to develop our main index.php file. Every page will be displayed by passing a GET variable to the index.php file. The index.php file will then screen the GET variable for harmful code and display the desired page if the code is found free of hacker contamination.

As you might now see, the index.php file is nothing more than a file with a long switch statement. The GET variable we will be passing to our scripts will be called page. Thus, the page variable will be analyzed by the switch statement. If you have a ton of pages, you'll need to put all the acceptable values for the GET variable into a database and query the database to determine if the value is relevant.

I am assuming you have an understanding of functions and switch statements so here is a code example below:

switch ($_GET['page'])
{

case 'home':

$legit = "NO";

$legit = is_legit();

if ($legit = "YES"){

include 'inc/home.php';

}

break;

This code looks to see what the value of the GET variable page is and if it has a value of home it starts to screen the data. The $legit variable is initialized to no and then the is_legit() function is called. In side the legit function you can screen other variables and do all sorts of data validation procedures. If your data is legit, the if statement kicks in and calls the home.php page. Every page is treated in this same manner, and this ensures the no hostile data is passed from the outside.

Protect PHP Code Step 3:
A final component we can incorporate from my last article on PHP security is the defining of a constant before we call the code. The code follows:

define("MyConstant ", true);
At the beginning of the code included we include the following statement:
if (!defined("MyConstant"))
{
die ("File Not Found");
}

Now we are able to define a constant if the data is legit and include our file. This stops outside intruders from easily hacking our PHP code.


If you have several pages making switch statements for each value simply isn't practical. You'll need to design a database and run a query against the stored acceptable values of your GET and POST data for each page. If the value is acceptable then you can just include the page dynamically. The following if statement, in conjunction with your prior database screening, could suffice for the entire switch statement.

if($query = "true"){
$legit = "NO";
$legit = is_legit();
if ($legit = "YES"){

include 'inc/home.php';
}
}

This way the user never sees any of the files you store in the inc folder and is forced to discover them by some other, much more difficult, means.

Anonymous FTP Setup!

Setting up an anonymous FTP server with proFTPD is simplicity itself - all you need to do is use the special ... block, which contains configuration parameters for the operation of your FTP server.

In order to enable anonymous FTP, pop open your "proftpd.conf" file and add the following code block to it:
# set root directory for anonymous users to /home/ftp

# set the user and group for the server process
User ftp
Group ftp
# alias "anonymous" login to "ftp"
UserAlias anonymous ftp
# restrict "anonymous" users from writing data


DenyAll


LINKS FOR THIS WORK
http://www.devshed.com/c/a/Administration/Professional-File-Transfer-with-proFTPD/
http://www.aerospacesoftware.com/proftpd.html


The "Headers Already Sent" error , a brief look at Headers!

Every beginner PHP programmer comes across the “headers already sent” error and then they google to find out how to get rid of it. What they don't try to understand is the root level logic behind that error. Surprisingly alot of junior PHP programmers have no idea what headers are.
I will try to explain in brief what headers are actually. I am not an expert on headers but what i put here is what i know from a little bit of experience.
Every HTTP/HTTPS web page request goes to the relevant web server (Apache , IIS etc ) and then the web server responds with the requested page. COOL!!! BUT how do the web server know what page is requested and what form data has been posted etc? HEADERS!
Headers are the means of communication between a browser and a web server. A normal web request from a browser for http://www.abc.com/index.php would look something like this in headers.
GET /index.php HTTP/1.1Host: www.abc.comConnection: CloseThe first line tells the web server that what transmission method is used GET or POST. Then the page name wanted and the protocol version used.The second line tells the web server what domain that page should be under, this way apache can look inside its virtual hosts list and see what domain resides in which directory. So if www.abc.com resides in /home/abc/public_html , then the page requested becomes /home/abc/public_html/index.php.
The third line tells the server that after the request is processed the connection will be closed.
So that was a simple request. Now its the servers turn to respond, in our case the page is a .php page , so the php engine will process the requested page and send the response. Before any of the html is sent back to the browser , PHP sends in some information about the server and the data coming going through. Here is a sample response header.
HTTP/1.0 200 OKDate: Sun, 25 Feb 2007 21:28:38 GMTServer: Microsoft-IIS/6.0Content-Type: text/htmlContent-Length: 4293Connection: close
First line gives the HTTP status code of the response. “200 OK” in this case. Which means all good , i found the requested page and i have processed it for you and i am going to send it through. A list of status codes can be found http://en.wikipedia.org/wiki/List_of_HTTP_status_codes. Next line is ofcourse the date and time of the server, then comes the name of the web server in use. Next line is content type , which you are probably already familiar with. Content type tells the browser what sort of data is going to come through , so that the browser can show it accordingly. In this case it is text/html , which the browser will show happily. Next comes the content length , ofcourse this is byte size of the data that is going to flow through. Last line as before tells the browser the connection will be closed once the data goes through.
Once the response header is complete , the php script output follows. So important thing to remember header first and then the data from your script.The PHP header() function basically you can add to the above header response from the server.For instance when you type in your php script..
header(”Location:login.php”);
You are basically adding a line to the above response header..
Location:login.php
Which means that please goto login.php for further processing and the browser obliges and heads to login.php.
When your php script even sends one ” ” (space) basically the data has started to flow to the users browser, as we know before any data flow the header goes out first. Hence that means the header has been sent to the browser, followed by your ” “(space) character. Now once the header has been sent and PHP sees some command like header(”Location:goto.php”) , PHP says … excuse me sir but you have already started to send output from your script , thus i have already sent out the response headers , now how do you expect me to add that line to the response header ?
To overcome this problem alot of programmers use ob_start() at the top , basically telling PHP to buffer the output until the end of the script or flushed explicitly with ob_end_flush or ob_flush. So basically no output is actually sent to the browser and the header command works fine. Now gentlemen that works fine and its a wonderful utility to have at your disposal BUT ….. if you are using ob_start() to get rid of header already sent errors , then you have a flow problem in your script. Fix the flow instead of just using ob_start as a quick band aid , you wont become a good programmer and last in the long run relying on shortcuts!
The ob_start function is best left to greater uses like using it for compression or using it with a callback function to process your output before sending out the script output or various other things.
Hope this article helps someone out there!

Setup an FTP user account minus shells

It's important to give to your strictly FTP users no real shell account on the Linux system. In this manner, if for any reasons someone could successfully get out of the FTP chrooted environment, it would not have the possibility of executing any user tasks since it doesn't have a bash shell. First, create new users for this purpose;

These users will be the users allowed to connect to your FTP server.
This has to be separate from a regular user account with unlimited access because of how the chroot environment works. Chroot makes it appear from the user's perspective as if the level of the file system you've placed them in is the top level of the file system.

Use the following command to create users in the /etc/passwd file. This step must be done for each additional new user you allow to access your FTP server.

        [root@deep ] /# mkdir /home/ftp
[root@deep ] /# useradd -d /home/ftp/ftpadmin/ -s /dev/null ftpadmin > /dev/null 2>&1
[root@deep ] /# passwd ftpadmin

Changing password for user ftpadmin
New UNIX password:
Retype new UNIX password:
passwd: all authentication tokens updated successfully

  • The mkdir command will create the ftp directory under the /home directory to handle all FTP users' home directories we'll have on the server.

  • The useradd command will add the new user named ftpadmin to our Linux server.

  • Finally, the passwd command will set the password for this user ftpadmin.

Once the home/ftp/ directory has been created you don't have to use this command again for additional FTP users.
  1. Edit the /etc/shells file, vi /etc/shells and add a non-existent shell name like null, for example. This fake shell will limit access on the system for FTP users.

              [root@deep ] /# vi /etc/shells

    /bin/bash
    /bin/sh
    /bin/ash
    /bin/bsh
    /bin/tcsh
    /bin/csh
    /dev/null
    /dev/null, This is our added no-existent shell. With Red Hat Linux, a special device name /dev/null exists for purposes such as these.

  2. Now, edit your /etc/passwd file and add manually the /./ line to divide the /home/ftp directory with the /ftpadmin directory where the user ftpadmin should be automatically chdir'd to. This step must be done for each FTP user you add to your passwd file.

              ftpadmin:x:502:502::/home/ftp/ftpadmin/:/dev/null
    To read:
              ftpadmin:x:502:502::/home/ftp/./ftpadmin/:/dev/null
    ^^
    The account is ftpadmin, but you'll notice the path to the home directory is a bit odd. The first part /home/ftp/ indicates the filesystem that should be considered their new root directory. The dot . divides that from the directory they should be automatically chdir'd. change directory'd into, /ftpadmin/.

Once again, the /dev/null part disables their login as a regular user. With this modification, the user ftpadmin now has a fake shell instead of a real shell resulting in properly limited access on the system.



http://www.faqs.org/docs/securing/chap29sec295.html

This morning I woke up and found that my site was gone. I'm not just talking about the server being down here - no, the site (or better, my blog's contents) was gone. I was getting database errors left and right (perhaps that means I need to do some better error catching in xBlog, eh?), so I logged into phpMyAdmin. Or I tried anyway. I wasn't able to log in, so I logged into cPanel for this domain. I went to the MySQL database section, and much to my dismay, none of my databases showed up. At this point, I officially freaked out. I immediately went to the support center for my hosting, and saw that someone else was having the same problems. There was hope, at least it wasn't just my databases. One of the server administrators came on, and was simply able to restart the SQL server to fix the problem. Thankfully, all my data was still intact.
I did, however, learn a most important lesson (one that I should have already known). Backup you data. Since I didn't want to have to manually go into phpMyAdmin and backup the database for xBlog each day, I started looking at what I'd have to do to write a PHP script to do this. A bit of research showed me that the easiest way would be to use mysqldump, with a shell command.
So, I rolled up my sleeves and wrote the following PHP function to make automatically backing up MySQL databases an easy thing.
/* Function to backup a mysql database table
* @param host
* The database host name (server)
* @param user
* Database user
* @param password
* Database password
* @param table
* Database table name
* @param backup_path
* The path to backup directory
* @param backup_name
* The name of the backup file - no extension
*/
function mysql_backup($host, $user, $password, $table, $backup_path, $backup_name) {
$day = date('w');
# gzip (.gz)
$backup = $backup_path.$backup_name.'_'.$day.'.gz';
exec(sprintf('mysqldump --host=%s --user=%s --password=%s %s --quick --lock-tables --add-drop-table gzip > %s', $host, $user, $password, $table, $backup));
# sql (.sql)
//$backup = $backup_path.$backup_name.'_'.$day.'.sql';
//exec(sprintf('mysqldump --host=%s --user=%s --password=%s %s --quick --lock-tables --add-drop-table > %s', $host, $user, $password, $table, $backup));
}
mysql_backup('localhost', 'user', 'password', 'xblogpro', '/root/path/to/backups/', 'backup');
exec("/usr/bin/lynx http://xxx.xxx.xxx.xxx/backups/mysql_backup_grabber.php");
?>
The parameters that are passed are explained in the script, so I won't go into detail on what those are again. For more information on the various options I passed to the mysqldump command, take a look at the documentation. The exec function runs a system command that will tell the Lynx browser to run the PHP script that is on my local server, which is where I wanted to back the files up to. I chose to use this, as I really wouldn't have felt all that safe with my backups sitting on the same server as the MySQL server. If you aren't in need of such things, simply remove the exec function to only backup the data to the regular server.
You'll see that I have two different exec commands. The one that is uncommented is the one that I'm using. It will gzip the files right away. If you'd rather just export a .sql file, simply comment out the gzip lines, and uncomment the sql lines.
I chose to append the day of the week to each backup, so at any given point, I'll have the last 7 days backed up. I know the chances of something happening while the script is in the process of backing up the data is rather slim, but I'd rather not risk it.
The contents of mysql_content_grabber.php is very straight forward. I simply copy the file from the remote sever to my local server. Here it is (it must run on the local server, obviously):
$day = date('w', time()+4000);
copy(http://www.anuragbhatia.org/backups/backup_$day.gz,
$_SERVER['DOCUMENT_ROOT']."/backups/backup_$day.gz");
?>
Since I am located in CST, and the server is in EST, I added a little over an hour to the time on this script. You may need to adjust for the timezone difference between servers. Why a little over an hour? 'cause my local computer's clock and the remote server's clocks are probably not exactly synchronized. ;)
Finally, I set up a cron job to run the script every night at midnight. The cron looks something like this:0 0 * * * php /root/path/to/backups/mysql_backup.php
There you have it. A quick and dirty way to automatically backup your MySQL databases with PHP. Hope it'll save a headache or two.

Edit: A small update was made to the script. The original used a header redirect to call the script that is running on my local host, however, that was not working with the cron job. I now use a system command to call the Lynx browser, which works much better.

Scalability with php applications

The following methods can help improve scalability with php applications.

1) object code caching
Each time a request comes to your server for a php script, it has to go through the compiler and then execute the object code. If this is cached, the 1st step is skipped and you end up with a faster and more responsive script.
There are many object code caching packages available on the Internet (some free, some commercial):
A) Ioncube: http://www.ioncube.com/
B) Zend Encoder: http://www.zend.com/products/zend_safeguard
C) Turckl MMCache: http://freshmeat.net/projects/turck-mmcache/

2) Template systems
Template systems provide a different type of caching. Content caching. Template systems work well in a situation where there is static data on one or many of your pages that doesn’t have to be reloaded. Caching systems also provide a separation of code and html, which will not only improve completion time of the overall project, but make it easier for future improvments. Most template systems for php are available for free:
A) Smarty Templates: http://smarty.php.net/
B) Pear Templates: http://pear.php.net/package/html_template_it/redirected
C) PHP savant: http://phpsavant.com/yawiki/

3) Distributed object caching systems
The most widely used system of this type is memcached (http://www.danga.com/memcached/).
This type of system makes your overall site faster by caching the majority of your database data into a large memory pool.
an interesting excerpt from their site:
Danga Interactive developed memcached to enhance the speed of LiveJournal.com, a site which was already doing 20 million+ dynamic page views per day for 1 million users with a bunch of webservers and a bunch of database servers. memcached dropped the database load to almost nothing, yielding faster page load times for users, better resource utilization, and faster access to the databases on a memcache miss.”

4) PHP variables that can be set
variables_order = ‘GPC’
register_argc_argv = ‘Off’
register_globals = ‘Off’ (this is a good idea to keep off for security purposes as well)always_populate_raw_post_data = ‘Off’
magic_quotes_gpc = ‘Off’
Disable Error Logging. This is a good idea to keep on when you are developing your scripts, but it has been known to decrease overall performance.
Use IP address to access your database. Although this is sometimes not possible, you will get a slight boost in lookup speed if the IP address is used to access your database rather than its hostname.

5) Output Compression
Almost all browsers these days support something called gzip compression. Gzip compression can decrease the overall size of your output by up to 80%, but with a tradeoff: cpu usage will go up by around 10%. The benefit of using this compression type is the fact that not only will your bandwidth be decreased, but your pages will load faster.
enabling it in php (add the following lines to php.ini):
zlib.output_compression = Onzlib.output_compression_level = (level) (where level is 1-9. You may want to try different values to see what is best for your system).
if you are using apache, you can also enable the mod_gzip module. It is highly configurable, with the ability to modify output based on MIME types, files, or browser settings.
6) Other things that may help
when using a database, only retrieve the data that you are actually going to use. This may sound like a no-brainer, but I have often times worked on projects where the original programmer used (select * from mytable) when they could have used (select fieldIneed from mytable).
index database tables whenever possible
Learn more about this Here
specific language tricks
An interesting blog article I found mentions many interesting tricks that can be used: http://ilia.ws/archives/12-PHP-Optimization-Tricks.html
an article on zend.com about measuring performance: http://www.zend.com/zend/trick/trick-optimizing-php.php

PHP Freelance Work

Are you looking for ideas on how to start offering your PHP skills for money? Here are a few links I've come across offering PHP (and other) freelance opportunities for the aspiring entrepreneur.nb: if you end up on a page that doesn't look obviously PHP related, enter PHP as the search term.

www.elance.com

www.freelancers.net

www.rentacoder.com

www.jobvertise.com

www.allfreelancework.com

work-at-home-jobs-xcelerator.com

http://www.mojolin.com/

http://www.phpcareer.com/jobs.php

http://hotjobs.yahoo.com/jobseeker/jobsearch/search_results.html?keywords_any=php&kw=php&type=main&search_corp=1&search_agencies=1&country1=USA&page_number=1&order_by=salary&search_type_form=quick&updated_since=sixtydays&related_titles=6&intl=us&save_recent=1
http://jobsearch.monster.com/jobsearch.asp?cy=US&re=14&brd=1%2C1863&lid=&fn=&q=php&sort=rv&vw=b

http://www.totalfreelance.com/

http://www.guru.com/

listbid.com

devbistro.com

theitjobboard.com

php-freelancers.com

freelancesolutions.com

getafreelancer.com

scriptlance.com

projectsimple.com

projectlance.com

colance.com

codelance.com

freelancequotes.com

techfreelance.com

outsourcetoday.com

ukjobs.ostg.com

Freelance Work

ifreelancers.com

scriptverse.com

odesk.com

RandomLib - A random class

RandomLib - A random class
The class can be used to select one or a group of item(strings, objects, anything) from the entire collection of items. It include randomly select, randomly select unique.
It also include function to randomly select weighted items and randomly select unique weighted items. Weighted items are items assigned a weight, so it can show up more frequently than ones with lower weight. This could be great on making sites need reward some user more than other, or PHP games.

/*********************************
RandomLib Version 1.0
Programmed by : Chao Xu(Mgccl)
E-mail : mgcclx@gmail.com
Website : http://www.webdevlogs.com
Info : Please email me if there is any feature you want
or there is any bugs. I will fix them as soon as possible.
*********************************/

class random{
var $data = array();
function add($string,$weight=1){
$this->data[] = array('s' => $string, 'w' => $weight);
}
function optimize(){
foreach($this->data as $var){
if($new[$var['s']]){
$new[$var['s']] += $var['w'];
}else{
$new[$var['s']] = $var['w'];
}
}
unset($this->data);
foreach($new as $key=>$var){
$this->data[] = array('s' => $key, 'w' => $var);
}
}

function select($amount=1){
if($amount == 1){
$rand = array_rand($this->data);
$result = $this->data[$rand]['s'];
}else{
$i = 0;
while($i<$amount){
$result[] = $this->data[array_rand($this->data)]['s'];
++$i;
}
}
return $result;
}

function select_unique($amount=1){
if($amount == 1){
$rand = array_rand($this->data);
$result = $this->data[$rand]['s'];
}else{
$rand = array_rand($this->data, $amount);
foreach($rand as $var){
$result[] = $this->data[$var]['s'];
}
}
return $result;
}

function select_weighted($amount=1){
$count = count($this->data);
$i = 0;
$max = -1;
while($i < $count){
$max += $this->data[$i]['w'];
++$i;
}
if(1 == $amount){
$rand = mt_rand(0, $max);
$w = 0; $n = 0;
while($w <= $rand){
$w += $this->data[$n]['w'];
++$n;
}
$key = $this->data[$n-1]['s'];
}else{
$i = 0;
while($i<$amount){
$random[] = mt_rand(0, $max);
++$i;
}
sort($random);
$i = 0;
$n = 0;
$w = 0;
while($i<$amount){
while($w<=$random[$i]){
$w += $this->data[$n]['w'];
++$n;
}
$key[] = $this->data[$n-1]['s'];
++$i;
}
}
return $key;
}

function select_weighted_unique($amount=1){
$count = count($this->data);
$i = 0;
if($amount >= $count){
while($i < $count){
$return[] = $this->data[$i]['s'];
++$i;
}
return $return;
}else{
$max = -1;
while($i < $count){
$max += $this->data[$i]['w'];
++$i;
}

$i = 0;
while($i < $amount){
$max -= $sub;
$w = 0;
$n = 0;
$num = mt_rand(0,$max);
while($w <= $num){
$w += $this->data[$n]['w'];
++$n;
}
$sub = $this->data[$n-1]['w'];
$key[] = $this->data[$n-1]['s'];

unset($this->data[$n-1]);
$this->data = array_merge($this->data);
++$i;
}
return $key;
}
}
}
?>



example useage

$timeparts = explode(' ',microtime());
$starttime = $timeparts[1].substr($timeparts[0],1);
include ('class.random.php');
$random = new random;
$random->add('this have weight 1000, ', 1000);
$random->add('this have weight 1, ', 1);
$random->add('this have weight 200, ', 200);
$random->add('this have weight 500, ', 500);
?>
The random items:

one weights 1000, one weights 1, one weights 200, one weights 500

Result for randomly select 1 item:select()?>

Result for randomly select 3 item:select(3))?>

Result for randomly select 3 unique item:select_unique(3))?>

Result for weighted randomly select 1 item:select_weighted()?>

Result for weighted randomly select 3 item:select_weighted(3))?>

Result for weighted randomly select 3 unique item:select_weighted_unique(3))?>

$timeparts = explode(' ',microtime());
$endtime = $timeparts[1].substr($timeparts[0],1);
echo 'time spent',bcsub($endtime,$starttime,6);
?>

COUNTER