\documentclass[solutions]{ictlab}

\RCS $Revision: 1.2 $

\usepackage{verbatim,alltt}
\usepackage[pdfpagemode=None,pdfauthor={Nick Urbanik}]{hyperref}

\newcommand*{\labTitle}{Using Regular Expressions to Create User Accounts}

\begin{document}
For each exercise where you write a program, keep the original program
from each exercise and modify a copy for the next exercise.

\noindent%
\textbf{Submission of assignment:}
You will need to submit all your programs by email to me at
\url{nicku@vtc.edu.hk}.  See the subject web site for the format of
the subject line of your assignments.

\noindent%
\textbf{Due:}
The deadline for submission is 12.30\,pm on the Tuesday of the
following week.


% \paragraph{Background:}

% There are many ways of creating accounts on a Linux system; your
% program could select the next available user and group \acro{ID}s,
% manually edit the \texttt{/etc/passwd}, \texttt{/etc/group} and
% \texttt{/etc/shadow} files, create the user directories, copy the
% login scripts and other basic account files from \texttt{/etc/skel},
% and change the ownership to the new account, but a simpler and more
% portable way is to use the standard \texttt{useradd} program that you
% learned about last year.  We can call that from our own Perl programs
% using the built-in \texttt{system} function.

% Today we will write a program that will create a user account for each
% student listed in the artificial student registration data.

% \paragraph{Procedure:}
 
\begin{enumerate}
\item Download the artificial student data from
  \url{http://nicku.org/snm/lab/regular-expressions/artificial-student-data.txt}.
  This file is in the old format of the student registration system,
  but contains no real data about any student.

\item Write a Perl program that can read all the lines of this file
  when it is given as a command line parameter, and print the student
  numbers only, one to each line.
\begin{verbatim}
#! /usr/bin/perl -w

while ( <> )
{
    print "$1\n" if /(\d{9})/;
}
\end{verbatim}%$

\item Make a copy of your program, and modify it so that it prints
  only the names of the students, one to each line, with no extra
  spaces either at the beginning or end of each name.
\begin{verbatim}
#! /usr/bin/perl -w

while ( <> )
{
    print "$1\n" if /^.{6}(.*[^ ])  +[MF]  \d{9} /;
}
\end{verbatim}%$

\item Using the manual page for the \texttt{useradd} program as a
  guide, modify your previous programs so that your program prints one
  \texttt{useradd} command for each student, using the student
  \acro{ID} as the login \acro{ID}, the Hong Kong \acro{ID} as the
  password, and the name of the student as a comment.
\begin{verbatim}
#! /usr/bin/perl -w

use strict;

# Note: I provided this subroutine on the subject website
# I did not intend that you should need to write it;
# I simply forgot that useradd requires a hashed password, not
# a plain password.
sub hash_md5_password($)
{
    my $clear_text_password = shift;
    my $salt = join '',
    ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64, rand 64, rand 64,
                                         rand 64, rand 64, rand 64, rand 64];
    $salt = '$1$'.$salt.'$';
    my $hashed_password = crypt( $clear_text_password, $salt );
    return $hashed_password;
}

while ( <> )
{
    if ( /^.{6}(.*[^ ])  +[MF]  (\d{9})  ([a-zA-Z]\d{6}\([\dA-Z]\))/ )
    {
        my ( $name, $id, $hkid ) = ( $1, $2, $3 );
        my $hashed_passwd = hash_md5_password( $hkid );
        print "useradd -c \"$name\" -p $hashed_passwd $id\n";
    }
}
\end{verbatim}%$

\item Modify this last program further so that it uses the built-in
  Perl function \texttt{system} to execute the \texttt{useradd}
  command as well as print the command.  Type \texttt{perldoc -f
    system} to read about this important function.
\begin{verbatim}
#! /usr/bin/perl -w

use strict;

sub hash_md5_password($)
{
    my $clear_text_password = shift;
    my $salt = join '',
    ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64, rand 64, rand 64,
                                         rand 64, rand 64, rand 64, rand 64];
    $salt = '$1$'.$salt.'$';
    my $hashed_password = crypt( $clear_text_password, $salt );
    return $hashed_password;
}

while ( <> )
{
    if ( /^.{6}(.*[^ ])  +[MF]  (\d{9})  ([a-zA-Z]\d{6}\([\dA-Z]\))/ )
    {
        my ( $name, $id, $hkid ) = ( $1, $2, $3 );
        my $hashed_passwd = hash_md5_password( $hkid );
        my @cmd = (
                   'useradd',
                   '-c', "\"$name\"",
                   '-p', $hashed_passwd,
                   $id
                  );
        print "@cmd\n";
        system @cmd;
    }
}
\end{verbatim}%$

\item Modify your last program so that it reports an error message if
  the execution of any \texttt{useradd} command is unsuccessful.
\begin{verbatim}
#! /usr/bin/perl -w

use strict;

sub hash_md5_password($)
{
    my $clear_text_password = shift;
    my $salt = join '',
    ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64, rand 64, rand 64,
                                         rand 64, rand 64, rand 64, rand 64];
    $salt = '$1$'.$salt.'$';
    my $hashed_password = crypt( $clear_text_password, $salt );
    return $hashed_password;
}

while ( <> )
{
    if ( /^.{6}(.*[^ ])  +[MF]  (\d{9})  ([a-zA-Z]\d{6}\([\dA-Z]\))/ )
    {
        my ( $name, $id, $hkid ) = ( $1, $2, $3 );
        my $hashed_passwd = hash_md5_password( $hkid );
        my @cmd = (
                   'useradd',
                   '-c', "\"$name\"",
                   '-p', $hashed_passwd,
                   $id
                  );
        print "@cmd\n";
        system( @cmd ) == 0 or warn( "@cmd failed\n" );
    }
}
\end{verbatim}%$
\end{enumerate}
\end{document}
