Compare commits
1 Commits
dev
...
feature/ja
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5dc7d88b7f |
19
CONTRIBUTING.md
Normal file
19
CONTRIBUTING.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Contributing
|
||||
|
||||
Contributions from the community are welcome! To contribute:
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a new branch for your feature or bug fix.
|
||||
3. Make your changes with proper testing.
|
||||
4. Submit a pull request detailing your modifications.
|
||||
|
||||
### Developer Documentation
|
||||
|
||||
Developer documentation is generated using JavaDoc. To generate and view the documentation:
|
||||
|
||||
```bash
|
||||
chmod +x generate_javadoc.sh
|
||||
./generate_javadoc.sh
|
||||
```
|
||||
|
||||
This will create a `docs/` folder with HTML documentation you can view in your browser.
|
||||
139
generate_javadoc.sh
Executable file
139
generate_javadoc.sh
Executable file
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bash
|
||||
# --- Generate JavaDoc for cflash ---
|
||||
# This script generates developer documentation using javadoc.
|
||||
# If JDK is missing, it prompts the user to install it using the detected package manager.
|
||||
|
||||
set -e
|
||||
|
||||
SRC_DIR="src/main/java"
|
||||
DOC_DIR="docs"
|
||||
|
||||
# --- Detect package manager ---
|
||||
detect_package_manager() {
|
||||
if command -v pacman >/dev/null 2>&1; then
|
||||
echo "pacman"
|
||||
elif command -v apt >/dev/null 2>&1; then
|
||||
echo "apt"
|
||||
elif command -v dnf >/dev/null 2>&1; then
|
||||
echo "dnf"
|
||||
elif command -v yum >/dev/null 2>&1; then
|
||||
echo "yum"
|
||||
elif command -v zypper >/dev/null 2>&1; then
|
||||
echo "zypper"
|
||||
elif command -v brew >/dev/null 2>&1; then
|
||||
echo "brew"
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
echo "apk"
|
||||
elif command -v emerge >/dev/null 2>&1; then
|
||||
echo "emerge"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Generate JDK install command ---
|
||||
jdk_install_command() {
|
||||
local pm="$1"
|
||||
case "$pm" in
|
||||
pacman) echo "pacman -Sy --noconfirm jdk-openjdk" ;;
|
||||
apt) echo "apt update && apt install -y openjdk-21-jdk || apt install -y default-jdk" ;;
|
||||
dnf) echo "dnf install -y java-21-openjdk-devel || dnf install -y java-latest-openjdk-devel" ;;
|
||||
yum) echo "yum install -y java-21-openjdk-devel || yum install -y java-latest-openjdk-devel" ;;
|
||||
zypper) echo "zypper install -y java-21-openjdk-devel || zypper install -y java-latest-openjdk-devel" ;;
|
||||
brew) echo "brew install openjdk" ;;
|
||||
apk) echo "apk add openjdk21" ;;
|
||||
emerge) echo "emerge dev-java/openjdk-bin" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Check for javadoc ---
|
||||
if ! command -v javadoc >/dev/null 2>&1; then
|
||||
echo "Java Development Kit (JDK) with javadoc not found."
|
||||
PM=$(detect_package_manager)
|
||||
|
||||
if [[ -z "$PM" ]]; then
|
||||
echo "Please install the latest JDK manually and rerun this script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CMD=$(jdk_install_command "$PM")
|
||||
if [[ $EUID -ne 0 && "$PM" != "brew" ]]; then
|
||||
echo "Please rerun this script with sudo if you want automatic JDK installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prompt user
|
||||
read -rp "Do you want to run the following command to install the JDK? [$CMD] (y/n): " ANSWER
|
||||
case "$ANSWER" in
|
||||
y|Y)
|
||||
echo "Installing JDK..."
|
||||
eval "$CMD"
|
||||
echo "JDK installed successfully."
|
||||
;;
|
||||
*)
|
||||
echo "JDK installation cancelled. Please install manually and rerun."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# --- Check if source directory exists ---
|
||||
if [[ ! -d "$SRC_DIR" ]]; then
|
||||
echo -e "\033[31mSource directory '$SRC_DIR' not found!"
|
||||
echo "Please ensure you're running this script from the project root directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Generate JavaDoc ---
|
||||
echo "Generating JavaDoc for cflash..."
|
||||
rm -rf "$DOC_DIR"
|
||||
mkdir -p "$DOC_DIR"
|
||||
|
||||
# Check if Maven is available
|
||||
if command -v mvn >/dev/null 2>&1; then
|
||||
echo "Using Maven to generate JavaDoc with dependencies..."
|
||||
if mvn javadoc:javadoc -Dquiet=true > /dev/null 2>&1; then
|
||||
# Copy generated docs from Maven location to our docs directory
|
||||
if [[ -d "target/reports/apidocs" ]]; then
|
||||
cp -r target/reports/apidocs/* "$DOC_DIR/"
|
||||
echo -e "\033[32mJavaDoc generated successfully using Maven!"
|
||||
echo -e "\033[0m→ Open file://$PWD/$DOC_DIR/index.html to view it."
|
||||
else
|
||||
echo -e "\033[31mMaven generated docs but couldn't find them in target/reports/apidocs"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "\033[31mMaven JavaDoc generation failed."
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Maven not found. Attempting to generate JavaDoc without dependencies..."
|
||||
# Try to download JLine dependency manually
|
||||
JLINE_JAR="$HOME/.m2/repository/org/jline/jline/3.25.1/jline-3.25.1.jar"
|
||||
if [[ -f "$JLINE_JAR" ]]; then
|
||||
echo "Found JLine dependency in Maven local repository..."
|
||||
CLASSPATH="$SRC_DIR:$JLINE_JAR"
|
||||
else
|
||||
echo "JLine dependency not found. JavaDoc may have missing links."
|
||||
CLASSPATH="$SRC_DIR"
|
||||
fi
|
||||
|
||||
if javadoc -quiet -d "$DOC_DIR" \
|
||||
-sourcepath "$SRC_DIR" \
|
||||
-classpath "$CLASSPATH" \
|
||||
-subpackages org.cametendo \
|
||||
-private \
|
||||
-author \
|
||||
-version \
|
||||
-doctitle "cflash - Disk Image Flashing Utility" \
|
||||
-windowtitle "cflash Documentation" \
|
||||
-bottom "Copyright © 2026 Cametendo. All rights reserved." > /dev/null 2>&1; then
|
||||
echo -e "\033[32mJavaDoc generated successfully!"
|
||||
echo -e "\033[0m→ Open file://$PWD/$DOC_DIR/index.html to view it."
|
||||
else
|
||||
echo -e "\033[31mJavaDoc generation failed. Install Maven for better dependency handling."
|
||||
echo "Run: apt install maven (Ubuntu/Debian) or pacman -S maven (Arch)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@@ -1,10 +1,32 @@
|
||||
package org.cametendo;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Utility class for managing block size configuration for disk flashing operations.
|
||||
*
|
||||
* <p>This class provides methods to both interactively prompt users for block size selection
|
||||
* and to map command-line inputs to valid block size values. The default block size is 4M.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class BlockSize {
|
||||
|
||||
/**
|
||||
* Default block size string value.
|
||||
* Set to "4M" as the default block size for flashing operations.
|
||||
*/
|
||||
public static String blockSizeString = "4M";
|
||||
|
||||
/**
|
||||
* Maps user input to a valid block size value.
|
||||
*
|
||||
* <p>Supports both numeric inputs (1-6) and string inputs (512K,1M,2M,4M,8M,16M).
|
||||
* If the input is not recognized, returns the default block size.</p>
|
||||
*
|
||||
* @param input User input string, either numeric (1-6) or block size string
|
||||
* @return Valid block size string (512K, 1M, 2M, 4M, 8M, 16M, or default)
|
||||
*/
|
||||
public static String mapBlockSize(String input) {
|
||||
return switch (input) {
|
||||
case "1", "512K" -> "512K";
|
||||
@@ -17,6 +39,15 @@ public class BlockSize {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactively prompts the user to select a block size.
|
||||
*
|
||||
* <p>Displays a menu of available block sizes and maps the user's input
|
||||
* to a valid block size value using {@link #mapBlockSize(String)}.</p>
|
||||
*
|
||||
* @param UserInput Scanner object for reading user input
|
||||
* @return The selected block size string
|
||||
*/
|
||||
static String blockSize(Scanner UserInput) {
|
||||
System.out.println("Choose a block size (Default: 4M)");
|
||||
System.out.println("512KB (1), 1M (2), 2M (3), 4M (4), 8M (5), 16M (6)");
|
||||
|
||||
@@ -3,7 +3,26 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Executes the dd command for disk flashing operations.
|
||||
*
|
||||
* <p>This class handles the actual execution of the dd command with the configured
|
||||
* parameters (input file, output device, block size, and output flags). It provides
|
||||
* real-time output streaming and calls the OSDetector for completion messages.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Dd {
|
||||
|
||||
/**
|
||||
* Executes the dd command with the configured parameters.
|
||||
*
|
||||
* <p>Runs the dd command using sudo with the specified image file, target device,
|
||||
* block size, and output flags. Streams the command output in real-time to show
|
||||
* progress. Upon completion, calls {@link OSDetector#wishWell(String)} to display
|
||||
* a completion message based on the image file name.</p>
|
||||
*/
|
||||
public static void dd() {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder("sudo", "dd", "if=" + FilePathAdd.ImagePath, "of=" + StorageDeviceLister.fullPath, "bs=" + BlockSize.blockSizeString, "status=progress", "oflag=" + OflagHandler.oflagHandleString);
|
||||
|
||||
@@ -9,10 +9,32 @@ import org.jline.reader.impl.completer.FileNameCompleter;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
|
||||
/**
|
||||
* Handles file path input and validation for disk image files.
|
||||
*
|
||||
* <p>This class provides functionality to interactively prompt users for image file paths
|
||||
* with tab completion support, validate that files exist and are regular files, and
|
||||
* validate file paths from command-line arguments.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FilePathAdd {
|
||||
|
||||
/**
|
||||
* Stores the validated path to the selected image file.
|
||||
*/
|
||||
public static String ImagePath = "";
|
||||
|
||||
/**
|
||||
* Interactively prompts the user to select an image file path.
|
||||
*
|
||||
* <p>Uses JLine for enhanced terminal interaction with tab completion support.
|
||||
* Validates that the selected path points to an existing regular file.</p>
|
||||
*
|
||||
* @return The validated path to the selected image file
|
||||
* @throws IOException If there are I/O errors during terminal setup or file validation
|
||||
*/
|
||||
protected static String filePath() throws IOException {
|
||||
fileQuestion();
|
||||
|
||||
@@ -39,6 +61,15 @@ public class FilePathAdd {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and returns the full path to an image file.
|
||||
*
|
||||
* <p>Takes a file path, validates that it exists and is a regular file,
|
||||
* and returns the real path. Used for command-line argument validation.</p>
|
||||
*
|
||||
* @param ImagePath Path to the image file to validate
|
||||
* @return Full validated path to the file, or null if invalid
|
||||
*/
|
||||
public static String validateAndGetFile(String ImagePath) {
|
||||
try {
|
||||
Path path = Path.of(ImagePath);
|
||||
@@ -54,6 +85,12 @@ public class FilePathAdd {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the prompt for file path input.
|
||||
*
|
||||
* <p>Informs the user that they should enter the full path to their ISO/image file
|
||||
* and mentions that tab completion is supported for convenience.</p>
|
||||
*/
|
||||
protected static void fileQuestion() {
|
||||
System.out.println("Please enter the FULL Path of your ISO / Image. (Tab-completion supported)");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
package org.cametendo;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Handles the final confirmation and execution of the disk flashing process.
|
||||
*
|
||||
* <p>This class displays the current configuration to the user for confirmation
|
||||
* before proceeding with the actual flashing operation using the dd command.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Flasher {
|
||||
|
||||
/**
|
||||
* Displays the current flashing configuration and prompts for user confirmation.
|
||||
*
|
||||
* <p>Shows the device path, image file path, block size, and output flag that will be used
|
||||
* for the flashing operation. If the user confirms, proceeds with the flashing process
|
||||
* by calling {@link Dd#dd()}. If the user cancels, exits the application.</p>
|
||||
*
|
||||
* @param UserInput Scanner object for reading user confirmation
|
||||
*/
|
||||
public static void flasher(Scanner UserInput) {
|
||||
|
||||
String input = "";
|
||||
|
||||
@@ -2,7 +2,25 @@ package org.cametendo;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Handles the initial greeting and user confirmation for the cflash application.
|
||||
*
|
||||
* <p>This class provides a welcome message and prompts the user to confirm
|
||||
* whether they want to proceed with flashing an image to a storage device.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Greeting {
|
||||
|
||||
/**
|
||||
* Displays a welcome message and prompts for user confirmation.
|
||||
*
|
||||
* <p>Shows the cflash welcome message and asks the user if they want to
|
||||
* flash an image. If the user declines, exits the application.</p>
|
||||
*
|
||||
* @param UserInput Scanner object for reading user confirmation
|
||||
*/
|
||||
public static void greeting(Scanner UserInput) {
|
||||
System.out.println("Welcome to cflash!");
|
||||
System.out.println("Would you like to flash an image (Y/n)");
|
||||
|
||||
@@ -2,7 +2,43 @@ package org.cametendo;
|
||||
import java.io.IOException;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Main entry point for the cflash application.
|
||||
*
|
||||
* <p>cflash is a command-line utility for flashing disk images to storage devices.
|
||||
* It supports both interactive mode and command-line argument mode for automation.</p>
|
||||
*
|
||||
* <p>In interactive mode, the user is guided through device selection, file path input,
|
||||
* block size configuration, and output flag selection. In command-line mode, all parameters
|
||||
* can be specified as arguments for automated flashing.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* Main method that serves as the entry point for the cflash application.
|
||||
*
|
||||
* <p>The application can operate in two modes:</p>
|
||||
* <ul>
|
||||
* <li>Interactive mode: No arguments provided, user is guided through the process</li>
|
||||
* <li>Command-line mode: Four arguments provided for automated execution</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Command-line arguments format:</p>
|
||||
* <ol>
|
||||
* <li>Device name (e.g., "sda" without /dev/ prefix)</li>
|
||||
* <li>Image file path (absolute or relative path to ISO/image file)</li>
|
||||
* <li>Block size (1-6 or 512K,1M,2M,4M,8M,16M)</li>
|
||||
* <li>Output flag (1-4 or direct,dsync,sync,nocache)</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param args Command-line arguments. If 4 arguments are provided, runs in automated mode.
|
||||
* If no arguments, runs in interactive mode.
|
||||
* @throws InterruptedException If the flashing process is interrupted
|
||||
* @throws IOException If there are I/O errors during device/file validation
|
||||
*/
|
||||
public static void main(String[] args) throws InterruptedException, IOException {
|
||||
Scanner UserInput = new Scanner(System.in);
|
||||
if (args.length == 4) {
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
package org.cametendo;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Detects operating system types from image file names and displays completion messages.
|
||||
*
|
||||
* <p>This class analyzes the filename of disk images to identify the operating system
|
||||
* and displays a personalized completion message with some fun descriptions for various
|
||||
* Linux distributions, BSD variants, and other operating systems.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class OSDetector {
|
||||
|
||||
/**
|
||||
* Analyzes an image file path and displays a personalized completion message.
|
||||
*
|
||||
* <p>Extracts the filename from the provided path and attempts to identify the
|
||||
* operating system based on filename patterns. Displays a fun, personalized message
|
||||
* wishing the user well with their new OS installation.</p>
|
||||
*
|
||||
* @param imagePath Path to the image file that was flashed
|
||||
*/
|
||||
public static void wishWell(String imagePath) {
|
||||
String fileName = Path.of(imagePath).getFileName().toString().toLowerCase();
|
||||
|
||||
|
||||
@@ -1,10 +1,32 @@
|
||||
package org.cametendo;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Handles output flag (oflag) configuration for the dd command.
|
||||
*
|
||||
* <p>This class provides functionality to both interactively prompt users for output flag selection
|
||||
* and to map command-line inputs to valid oflag values. The default output flag is "direct".</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class OflagHandler {
|
||||
|
||||
/**
|
||||
* Default output flag string value.
|
||||
* Set to "direct" as the default output flag for dd operations.
|
||||
*/
|
||||
public static String oflagHandleString = "direct";
|
||||
|
||||
/**
|
||||
* Maps user input to a valid output flag value.
|
||||
*
|
||||
* <p>Supports both numeric inputs (1-4) and string inputs (direct,dsync,sync,nocache).
|
||||
* If the input is not recognized, returns the default output flag.</p>
|
||||
*
|
||||
* @param input User input string, either numeric (1-4) or output flag string
|
||||
* @return Valid output flag string (direct, dsync, sync, nocache, or default)
|
||||
*/
|
||||
public static String mapOflagHandle(String input) {
|
||||
return switch (input) {
|
||||
case "1", "direct" -> "direct";
|
||||
@@ -15,6 +37,15 @@ public class OflagHandler {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactively prompts the user to select an output flag.
|
||||
*
|
||||
* <p>Displays a menu of available output flags and maps the user's input
|
||||
* to a valid output flag value using {@link #mapOflagHandle(String)}.</p>
|
||||
*
|
||||
* @param UserInput Scanner object for reading user input
|
||||
* @return The selected output flag string
|
||||
*/
|
||||
static String Oflag(Scanner UserInput) {
|
||||
System.out.println("Choose an Oflag (Default: direct)");
|
||||
System.out.println("direct (1), dsync (2), sync (3), nocache (4)");
|
||||
|
||||
@@ -5,11 +5,38 @@ import java.io.InputStreamReader;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Handles storage device detection, listing, and validation for the flashing process.
|
||||
*
|
||||
* <p>This class provides functionality to list available storage devices using lsblk,
|
||||
* validate device paths, and interactively prompt users to select a target device
|
||||
* for flashing operations.</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class StorageDeviceLister {
|
||||
|
||||
/**
|
||||
* Stores the selected device name (without /dev/ prefix).
|
||||
*/
|
||||
public static String device = "";
|
||||
|
||||
/**
|
||||
* Stores the full validated path to the selected device.
|
||||
*/
|
||||
public static String fullPath = "";
|
||||
|
||||
/**
|
||||
* Interactively prompts the user to select a storage device.
|
||||
*
|
||||
* <p>Displays a list of available storage devices using the lsblk command,
|
||||
* then prompts the user to enter a device name. Validates the device path
|
||||
* and continues prompting until a valid device is selected.</p>
|
||||
*
|
||||
* @param UserInput Scanner object for reading user input
|
||||
* @return The full validated path to the selected device
|
||||
*/
|
||||
protected static String deviceCheck(Scanner UserInput) {
|
||||
deviceList();
|
||||
|
||||
@@ -33,6 +60,16 @@ public class StorageDeviceLister {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and returns the full path to a storage device.
|
||||
*
|
||||
* <p>Takes a device name (without /dev/ prefix), constructs the full path,
|
||||
* and validates that the device exists and is accessible. Used for command-line
|
||||
* argument validation.</p>
|
||||
*
|
||||
* @param deviceName Device name without /dev/ prefix (e.g., "sda")
|
||||
* @return Full validated path to the device, or null if invalid
|
||||
*/
|
||||
public static String validateAndGetPath(String deviceName) {
|
||||
try {
|
||||
Path path = Path.of("/dev/" + deviceName);
|
||||
@@ -43,6 +80,12 @@ public class StorageDeviceLister {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a list of available storage devices using the lsblk command.
|
||||
*
|
||||
* <p>Executes the lsblk command to show block devices and their properties,
|
||||
* then prompts the user to enter a device name for selection.</p>
|
||||
*/
|
||||
private static void deviceList() {
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder("lsblk");
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
package org.cametendo;
|
||||
|
||||
/**
|
||||
* Utility class for parsing yes/no user responses.
|
||||
*
|
||||
* <p>This class provides a simple method to interpret user input as a boolean
|
||||
* value, accepting various forms of "yes" and treating everything else as "no".</p>
|
||||
*
|
||||
* @author Cametendo
|
||||
* @version 1.0
|
||||
*/
|
||||
public class YesNo {
|
||||
|
||||
/**
|
||||
* Parses user input to determine if it represents a "yes" response.
|
||||
*
|
||||
* <p>Accepts "Y", "y", and empty string (default) as yes responses.
|
||||
* All other inputs are treated as no responses.</p>
|
||||
*
|
||||
* @param input User input string to parse
|
||||
* @return true if the input represents a yes response, false otherwise
|
||||
*/
|
||||
public static boolean check(String input) {
|
||||
switch (input) {
|
||||
case "Y":
|
||||
|
||||
Reference in New Issue
Block a user