Implements an image recognition captcha.

"; break; case 'admin/modules#description': case 'admin/modules/textimage': case 'admin/textimage': $output = t('Implements an image recognition captcha.'); break; } return $output; } function textimage_captchachallenge(&$form) { $form['captcha_response'] = array ( '#type' => 'textfield', '#title' => t('Captcha Validation'), '#default_value' => '', '#required' => TRUE, '#validate' => array('_captcha_validate' => array()), '#description' => t('Please type in the letters/numbers that are shown in the image above.'), '#prefix' => 'Captcha Image: you will need to recognize the text in it.', ); return $form; } function textimage_captchavalidate(&$captcha_word, &$correct) { $captcha_word = drupal_strtolower($captcha_word); if (($_SESSION['captcha'] != '') && $captcha_word == $_SESSION['captcha']) { $correct = true; } else { $correct = false; form_set_error('captcha_response', t('The image verification code you entered is incorrect.')); } } /** * Implementation of hook_menu(). */ function textimage_menu($may_cache) { $items = array(); $suffix = ''; if (arg(2)!=null) $suffix='/'.arg(2); $items[] = array( 'path' => '_textimage/image'.$suffix, 'title' => t('textimage'), 'callback' => '_textimage_image', 'access' => user_access('access textimages'), 'type' => MENU_CALLBACK ); return $items; } function textimage_perm() { return array('access textimages'); } function textimage_settings() { $fonts_path = variable_get("textimage_fonts_path", ""); $images_path = variable_get("textimage_images_path", ""); //check for GD if (!function_exists(imagecreate)) drupal_set_message(t('Image library not available. Textimage needs the GD library extension to be installed. Please install GD.')); //check for TTF support elseif (!function_exists(imagettftext)) drupal_set_message(t('Your image library does not seem to have TrueType font support. Textimage will work, but will use the default inbuilt font.'),'status'); //check for valid font path elseif ($fonts_path!="" && !is_dir($fonts_path)) drupal_set_message(t('The current font path is invalid. The default font will be used.')); //check for valid image path if ($images_path!="" && !is_dir($images_path)) drupal_set_message(t('The current images path is invalid. No images will be used.')); //Fonts settings $form['fonts'] = array( '#type' => 'fieldset', '#title' => t('Fonts settings'), '#collapsible' => TRUE, '#collapsed' => FALSE ); $form['fonts']['textimage_use_only_upper'] = array( '#type' => 'checkbox', '#title' => t('Use only Uppercase'), '#default_value' => variable_get('textimage_use_only_upper',0) ); $form['fonts']['textimage_fonts_path'] = array( '#type' => 'textfield', '#title' => t('TrueType Fonts Path'), '#default_value' => $fonts_path, '#size' => 30, '#maxlength' => 255, '#description' => t('Location of the directory where the Truetype (.ttf) fonts are stored. If you do not provide any fonts, the module will use the default font for text. Relative paths will be resolved relative to the Drupal installation directory.'), ); $form['fonts']['textimage_font_size'] = array( '#type' => 'textfield', '#title' => t('Font Size'), '#default_value' => variable_get('textimage_font_size',24), '#size' => 5, '#maxlength' => 2, '#description' => t('Font size of Captcha text (in pixels).'), '#validate' => array("_textimage_number_validate" => array("textimage_font_size")), ); $form['fonts']['textimage_char_spacing_max'] = array( '#type' => 'textfield', '#title' => t('Character Spacing'), '#default_value' => variable_get('textimage_char_spacing_max',10), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the kerning between letters in Captcha. Higher numbers indicate more spacing.'), '#validate' => array("_textimage_number_validate" => array("textimage_char_spacing_max")), ); $form['fonts']['textimage_char_jiggle_amount'] = array( '#type' => 'textfield', '#title' => t('Character Jiggle'), '#default_value' => variable_get('textimage_char_jiggle_amount',5), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of up and down movement in the Captcha letters. Higher numbers indicate more jiggling.'), '#validate' => array("_textimage_number_validate" => array("textimage_char_jiggle_amount")), ); $form['fonts']['textimage_char_rotate_amount'] = array( '#type' => 'textfield', '#title' => t('Character Rotation'), '#default_value' => variable_get('textimage_char_rotate_amount',5), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of rotation in the Captcha letters (in degrees, only works with non-default fonts).'), '#validate' => array("_textimage_number_validate" => array("textimage_char_rotate_amount")), ); $form['fonts']['textimage_char_size_amount'] = array( '#type' => 'textfield', '#title' => t('Character Size Adjustment'), '#default_value' => variable_get('textimage_char_size_amount',2), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of variation in size between the different letters in the Captcha (in pixels).'), '#validate' => array("_textimage_number_validate" => array("textimage_char_size_amount")), ); //Image settings $form['images'] = array( '#type' => 'fieldset', '#title' => t('Image settings'), '#collapsible' => TRUE, '#collapsed' => FALSE ); $form['images']['textimage_images_path'] = array( '#type' => 'textfield', '#title' => t('Background Images Path'), '#default_value' => $images_path, '#size' => 30, '#maxlength' => 255, '#description' => t('Location of the directory where the background images are stored. If you do not provide a directory, solid colors will be used. Relative paths will be resolved relative to the Drupal installation directory.'), ); $form['images']['textimage_image_noise'] = array( '#type' => 'textfield', '#title' => t('Image Noise (pixels)'), '#default_value' => variable_get('textimage_image_noise',4), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the amount of noise (random pixels) in the Captcha image. Higher numbers indicate more noise.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_noise")), ); $form['images']['textimage_image_lines'] = array( '#type' => 'textfield', '#title' => t('Image Noise (lines)'), '#default_value' => variable_get('textimage_image_lines',4), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the amount of noise (random lines) in the Captcha image. Higher numbers indicate more noise.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_lines")), ); $form['images']['textimage_image_margin'] = array( '#type' => 'textfield', '#title' => t('Image Margin'), '#default_value' => variable_get('textimage_image_margin',10), '#size' => 5, '#maxlength' => 4, '#description' => t('Set a distance between the Captcha letters and the edges of the image.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_margin")), ); $form['info'] = array( '#type' => 'fieldset', '#title' => t('Image and font information'), '#collapsible' => TRUE, '#collapsed' => FALSE ); if (isset($fonts_path)) { $imagefontinfo .= t('Number of fonts found: ').count(_textimage_font_list()); } if (isset($images_path)) { $imagefontinfo .= '
'.t('Number of background images found: ').count(_textimage_image_list()); } $gdinfo = gd_info(); $imagefontinfo .= '
'.t('GD Version: ').$gdinfo["GD Version"]; $imagefontinfo .= '
'.t(' FreeType Support: '); $imagefontinfo .= ($gdinfo["FreeType Support"]==true) ? 'True' : 'False'; $imagefontinfo .= '
'; $form['info']['captcha_info'] = array ( '#type' => 'item', '#value' => $imagefontinfo, ); return $form; } function textimage_settings_form_validate ($form_id,$form) { //check for valid font path if ($form['textimage_fonts_path'] !="" && !is_dir($form['textimage_fonts_path'])) form_set_error('textimage_fonts_path', t('The entered font path is invalid')); //check for valid image path if ($form['textimage_images_path'] !="" && !is_dir($form['textimage_images_path'])) form_set_error('textimage_images_path', t('The entered image path is invalid')); } function _textimage_number_validate ($field,$fieldName) { if (!is_numeric($field['#value'])) { form_set_error($fieldName,t("The value for")." ".t($field['#title'])." ".t("must be a number")); } } /** * Prints an image containing a textimage code. */ function _textimage_image() { //if we don't have GD2 functions, we can't generate the image if (!function_exists('imagecreatetruecolor')) return; // Set headers header('Expires: Mon, 01 Jan 1997 05:00:00 GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); header('Content-type: image/png'); $string = _textimage_code(); // Get truetype font list $fonts = _textimage_font_list(); // Get the background images list $images = _textimage_image_list(); // Randomization amounts: $charSpacingMax = variable_get('textimage_char_spacing_max',10); // Letter spacing max (pixels) $charSpacingMin = max($charSpacingMax*.5,0); // Letter spacing minimum (pixels) $charJiggleAmount = variable_get('textimage_char_jiggle_amount',5); // Up and down randomization (pixels) $charRotateAmount = variable_get('textimage_char_rotate_amount',5); // Character rotation amount (degrees) $charSizeAmount = variable_get('textimage_char_size_amount',2); // Character size amount (pixels) $imageRotateAmount = variable_get('captcha_image_rotate_amount',12); // Image rotation amount (degrees) // Static amounts: $charInitialSize = variable_get('textimage_font_size',24); // Initial Font $imageNoise = variable_get('textimage_image_noise',4); // Amount of noise added to image $imageLines = variable_get('textimage_image_lines',4); // Amount of noise added to image $imageMargin = variable_get('textimage_image_margin',10); // Margin around image (pixels) // write text using a truetype font if (function_exists(imagettftext) && count($fonts) > 0) { // Initialize variables for the loop $characterDetails = array(); // contains the final info about each character // Build a list of character settings for the captcha string for ($i=0;$i $charSize, "angle" => $charAngle, "x" => $x, "y" => $y, "color" => $foreground, "font" => $font, "char" => $char ); // Increment the image size $imageWidth = $x + $charWidth; $imageHeight = max($imageHeight,$y+$charJiggleAmount); } // Create the image based off the string length and margin if (count($images) > 0) { // We're going to be using an image, and need a tranparent background to start with $im = _textimage_create_transparent_image($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin); $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127); } else { // Just make a plain-jane color brackground $im = imagecreatetruecolor($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin); $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250)); $noisecolor = $background; imagefill($im, 0, 0, $background); } // Specify colors to be used in the image $foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80)); foreach($characterDetails as $char) { // draw character imagettftext($im,$char['size'],$char['angle'],$char['x']+$imageMargin,$char['y']+$imageMargin,$foreground,$char['font'],$char['char']); } } else { // write text using a built-in font $x = 0; $y = 0; $imageWidth = 60 + drupal_strlen($string)*$charSpacingMax*.35; $imageHeight = 30 + $charJiggleAmount; // Create the image if (count($images) > 0 && function_exists(imagecolorallocatealpha)) { // We're going to be using an image, and need a tranparent background to start with $im = _textimage_create_transparent_image($imageWidth, $imageHeight); $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127); } else { // Just make a plain-jane color brackground $im = imagecreatetruecolor($imageWidth, $imageHeight); $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250)); $noisecolor = $background; imagefill($im, 0, 0, $background); } // Add the text for ($i=0;$i 0) { // Prepare a larger image with a background image $im2 = _textimage_create_transparent_image($imageWidth, $imageHeight); } else { // Prepare a larger image with a solid color $im2 = imagecreatetruecolor($imageWidth, $imageHeight); imagefill($im2, 0, 0, $background); } $result = imagecopyresampled ($im2, $im, $imageMargin, $imageMargin, 0, 0, $imageWidth, $imageHeight, imagesx($im), imagesy($im)); $im = $im2; } // strikethrough imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground); // Add Noise for ($x=0; $x<$imageWidth; $x++) { for ($row=0; $row<$imageNoise;$row++) { $y = rand(0,$imageHeight); imagesetpixel($im, $x, $y, $noisecolor); } } // Add Lines and Ellipses for ($x=0; $x<$imageLines;$x++) { imageline($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor); imageellipse($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor); } // Fill image with a random background image if available if (count($images) > 0) { $image = $images[rand(0,count($images)-1)]; _textimage_apply_background_image($im,$image); } //output to browser imagepng($im); imagedestroy($im); } /** * Returns a random string for use in a captcha */ function _textimage_code() { $consts='bcdgjxvmnprst'; $vowels='aeiou'; for ($x=0; $x < 6; $x++) { mt_srand ((double) microtime() * 1000000); $const[$x] = drupal_substr($consts,mt_rand(0,drupal_strlen($consts)-1),1); $vow[$x] = drupal_substr($vowels,mt_rand(0,drupal_strlen($vowels)-1),1); } $string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4]; $string = drupal_substr($string,0,rand(4,6)); //everytime we create a new code, we write it to session $_SESSION['captcha'] = drupal_strtolower($string); if(variable_get('textimage_use_only_upper',0)) $string = drupal_strtoupper($string); return $string; } /** * Returns an array of files with TTF extensions in the specified directory. */ function _textimage_font_list() { $fontdir = variable_get("textimage_fonts_path", ""); $filelist = array(); if (is_dir($fontdir) && $handle = opendir($fontdir)) { while ($file = readdir($handle)) { if (preg_match("/\.ttf$/i",$file) == 1) $filelist[] = $fontdir.'/'.$file; } closedir($handle); } return $filelist; } /** * Returns an array of files with jpg, png, and gif extensions in the specified directory. */ function _textimage_image_list() { $imagesdir = variable_get("textimage_images_path", ""); $filelist = array(); if (is_dir($imagesdir) && $handle = opendir($imagesdir)) { while ($file = readdir($handle)) { if (preg_match("/\.gif|\.png|\.jpg$/i",$file) == 1) $filelist[] = $imagesdir.'/'.$file; } closedir($handle); } return $filelist; } /** * Overlays an image to the supplied image resource */ function _textimage_apply_background_image (&$imageResource,$imageFile) { $backgroundResource = image_gd_open($imageFile,substr($imageFile,-3)); // Copy the text onto the background $backX = imagesx($backgroundResource); $backY = imagesy($backgroundResource); $textX = imagesx($imageResource); $textY = imagesy($imageResource); $randomBackX = rand(0,$backX-$textX); $randomBackY = rand(0,$backY-$textY); // Place the text onto a random location of the background image imagecopyresampled($backgroundResource,$imageResource,$randomBackX,$randomBackY,0,0,$textX,$textY,$textX,$textY); // Crop the background image to the original image size imagecopyresampled($imageResource,$backgroundResource,0,0,$randomBackX,$randomBackY,$textX,$textY,$textX,$textY); } /** * Creates transparent image resources for images with graphic backgrounds */ function _textimage_create_transparent_image($x, $y) { $i = imagecreatetruecolor($x, $y); $b = imagecreatefromstring(base64_decode(_text_image_blankpng())); imagealphablending($i, false); imagesavealpha($i, true); imagecopyresized($i, $b ,0 ,0 ,0 ,0 ,$x, $y, imagesx($b), imagesy($b)); return $i; } function _text_image_blankpng() { $c = "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m"; $c .= "dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBNCg"; $c .= "dyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAAN"; $c .= "egcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQ"; $c .= "oHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAA"; $c .= "DXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII="; return $c; } ?> With War Crimes Argument Banned, Manning's Military Trial Is Judicial Lynching logo
Published on Tallahassee Network for Justice & Peace (http://tnjp.org)

With War Crimes Argument Banned, Manning's Military Trial Is Judicial Lynching

Created 06/11/2013 - 12:38pm

With War Crimes Argument Banned, Manning's Military Trial Is Judicial Lynching [1]
Monday, 10 June 2013 09:30 By Chris Hedges, Truthdig | Op-Ed

The military trial of Bradley Manning is a judicial lynching. The government has effectively muzzled the defense team. The Army private first class is not permitted to argue that he had a moral and legal obligation under international law to make public the war crimes he uncovered. The documents that detail the crimes, torture and killing Manning revealed, because they are classified, have been barred from discussion in court, effectively removing the fundamental issue of war crimes from the trial. Manning is forbidden by the court to challenge the government’s unverified assertion that he harmed national security. Lead defense attorney David E. Coombs said during pretrial proceedings that the judge’s refusal to permit information on the lack of actual damage from the leaks would “eliminate a viable defense, and cut defense off at the knees.” And this is what has happened.

Manning is also barred from presenting to the court his motives for giving the website WikiLeaks hundreds of thousands of classified diplomatic cables, war logs from Afghanistan and Iraq, and videos. The issues of his motives and potentially harming national security can be raised only at the time of sentencing, but by then it will be too late.

The draconian trial restrictions, familiar to many Muslim Americans tried in the so-called war on terror, presage a future of show trials and blind obedience. Our email and phone records, it is now confirmed, are swept up and stored in perpetuity on government computers. Those who attempt to disclose government crimes can be easily traced and prosecuted under the Espionage Act. Whistle-blowers have no privacy and no legal protection. This is why Edward Snowden—a former CIA technical assistant who worked for a defense contractor with ties to the National Security Agency and who leaked to Glenn Greenwald at The Guardian the information about the National Security Council’s top-secret program to collect Americans’ cellphone metadata, e-mail and other personal data—has fled the United States. The First Amendment is dead. There is no legal mechanism left to challenge the crimes of the power elite. We are bound and shackled. And those individuals who dare to resist face the prospect, if they remain in the country, of joining Manning in prison, perhaps the last refuge for the honest and the brave.

Coombs opened the trial last week by pleading with the judge, Army Col. Denise Lind, for leniency based on Manning’s youth and sincerity. Coombs is permitted by Lind to present only circumstantial evidence concerning Manning’s motives or state of mind. He can argue, for example, that Manning did not know al-Qaida might see the information he leaked. Coombs is also permitted to argue, as he did last week, that Manning was selective in his leak, intending no harm to national interests. But these are minor concessions by the court to the defense. Manning’s most impassioned pleas for freedom of information, especially regarding email exchanges with the confidential government informant Adrian Lamo, as well as his right under international law to defy military orders in exposing war crimes, are barred as evidence.

Manning is unable to appeal to the Nuremberg principles, a set of guidelines created by the International Law Commission of the United Nations after World War II to determine what constitutes a war crime. The principles make political leaders, commanders and combatants responsible for war crimes, even if domestic or internal laws allow such actions. The Nuremberg principles are designed to protect those, like Manning, who expose these crimes. Orders do not, under the Nuremberg principles, offer an excuse for committing war crimes. And the Nuremberg laws would clearly condemn the pilots in the “Collateral Murder” video and their commanders and exonerate Manning. But this is an argument we will not be allowed to hear in the Manning trial.

Manning has admitted to 10 lesser offenses surrounding his leaking of classified and unclassified military and State Department files, documents and videos, including the “Collateral Murder” video, which shows a U.S. Apache attack helicopter in 2007 killing 12 civilians, including two Reuters journalists, and wounding two children on an Iraqi street. His current plea exposes him to penalties that could see him locked away for two decades. But for the government that is not enough. Military prosecutors are pursuing all 22 charges against him. These charges include aiding the enemy, wanton publication, espionage, stealing U.S. government property, exceeding authorized access and failures to obey lawful general orders—charges that can bring with them 149 years plus life.

“He knew that the video depicted a 2007 attack,” Coombs said of the “Collateral Murder” recording. “He knew that it [the attack] resulted in the death of two journalists. And because it resulted in the death of two journalists it had received worldwide attention. He knew that the organization Reuters had requested a copy of the video in FOIA [Freedom of Information Act] because it was their two journalists that were killed, and they wanted to have that copy in order to find out what had happened and to ensure that it didn’t happen again. He knew that the United States had responded to that FOIA request almost two years later indicating what they could find and, notably, not the video.

“He knew that David Finkel, an author, had written a book called ‘The Good Soldiers,’ and when he read through David Finkel’s account and he talked about this incident that’s depicted in the video, he saw that David Finkel’s account and the actual video were verbatim, that David Finkel was quoting the Apache air crew. And so at that point he knew that David Finkel had a copy of the video. And when he decided to release this information, he believed that this information showed how [little] we valued human life in Iraq. He was troubled by that. And he believed that if the American public saw it, they too would be troubled and maybe things would change.”

“He was 22 years old,” Coombs said last Monday as he stood near the bench, speaking softly to the judge at the close of his opening statement. “He was young. He was a little naive in believing that the information that he selected could actually make a difference. But he was good-intentioned in that he was selecting information that he hoped would make a difference.”

“He wasn’t selecting information because it was wanted by WikiLeaks,” Coombs concluded. “He wasn’t selecting information because of some 2009 most wanted list. He was selecting information because he believed that this information needed to be public. At the time that he released the information he was concentrating on what the American public would think about that information, not whether or not the enemy would get access to it, and he had absolutely no actual knowledge of whether the enemy would gain access to it. Young, naive, but good-intentioned.”

The moral order is inverted. The criminal class is in power. We are the prey. Manning, in a just society, would be a prosecution witness against war criminals. Those who committed these crimes should be facing prison. But we do not live in a just society.

The Afghans, the Iraqis, the Yemenis, the Pakistanis and the Somalis know what American military forces do. They do not need to read WikiLeaks. They have seen the bodies, including the bodies of their children, left behind by drone strikes and other attacks from the air. They have buried the corpses of those gunned down by coalition forces. With fury, they hear our government tell lies, accounts that are discredited by the reality they endure. Our wanton violence and hypocrisy make us hated and despised, fueling the rage of jihadists and amassing legions of new enemies against the United States. Manning, by providing a window into the truth, opened up the possibility of redemption. He offered hope for a new relationship with the Muslim world, one based on compassion and honesty, on the rule of law, rather than the cold brutality of industrial warfare. But by refusing to heed the truth that Manning laid before us, by ignoring the crimes committed daily in our name, we not only continue to swell the ranks of our enemies but put the lives of our citizens in greater and greater danger. Manning did not endanger us. He sought to thwart the peril that is daily exacerbated by our political and military elite.

Manning showed us through the documents he released that Iraqis have endured hundreds of rapes and murders, along with systematic torture by the military and police of the puppet government we installed. He let us know that none of these atrocities were investigated. He provided the data that showed us that between 2004 and 2009 there were at least 109,032 “violent deaths” in Iraq, including those of 66,081 civilians, and that coalition troops were responsible for at least 195 civilian deaths in unreported events. He allowed us to see in the video “Collateral Murder” the helicopter attack on unarmed civilians in Baghdad. It was because of Manning that we could listen to the callous banter between pilots as the Americans nonchalantly fired on civilian rescuers. Manning let us see a U.S. Army tank crush one of the wounded lying on the street after the helicopter attack. The actions of the U.S. military in this one video alone, as law professor Marjorie Cohn has pointed out, violate Article 85 of the First Protocol to the Geneva Conventions, which prohibits the targeting of civilians, Common Article 3 of the Geneva Conventions, which requires that wounded be treated, and Article 17 of the First Protocol, which permits civilians to rescue and care for wounded without being harmed. We know of this war crime and many others because of Manning. And the decision to punish the soldier who reported these war crimes rather than the soldiers responsible for these crimes mocks our pretense of being a nation ruled by law.

“I believed if the public, particularly the American public, could see this, it could spark a debate on the military and our foreign policy in general as it applied to Iraq and Afghanistan,” Manning said Feb. 28 when he pleaded guilty to the lesser charges. He said he hoped the release of the information to WikiLeaks “might cause society to reconsider the need to engage in counterterrorism while ignoring the situation of the people we engaged with every day.”

But it has not. Our mechanical drones still circle the skies delivering death. Our attack jets still blast civilians. Our soldiers and Marines still pump bullets into mud-walled villages. Our artillery and missiles still raze homes. Our torturers still torture. Our politicians and generals still lie. And the man who tried to stop it all is still in prison.

Trial transcripts used for this report came from the nonprofit Freedom of the Press Foundation, which, because the government refused to make transcripts publicly available, is raising money to have its own stenographer at the trial. Transcripts from the pretrial hearing came from journalist Alexa O’Brien.