Digital Colony!

Test Using EXISTS Before Inserting

Here is a sample stored procedure that prevents duplicate inserts with the same value by first performing a test using SQL Server's EXISTS statement.
IF OBJECT_ID('prcTagNameInsert') IS NOT NULL
    DROP PROCEDURE prcTagNameInsert
GO
CREATE PROCEDURE prcTagNameInsert
    @tagName    VARCHAR(50)
AS

IF NOT EXISTS(SELECT 1 FROM Tag WHERE tagName = @tagName)
    INSERT INTO Tag (tagName) 
    VALUES (@tagname)

Labels:

 

ASP.NET Email Using VB

This article is for ASP.NET 1.x. For a 2.0 snippet read Sending Email in ASP.NET 2.0 (VB.NET).

By now we all know how to send server-side email using classic ASP. We either use Microsoft's CDONTS technology or a third-party component such as ServerObjects's ASPMail or Persist's ASPEmail. With ASP.NET server-side email is built-in and can be accessed using the System.Web.Mail namespace. In this article, I will demonstrate how to generate an email with form validation.

The Example

A lot of people post their resume on their web site. The advantage of having a resume available for recruiters and potential employers on a web site is valuable. The downside is you don't know who is reading your resume. A simple way to resolve that is to set up a form where the user requests that the resume be emailed to them. Then you can blind carbon copy yourself and you'll know exactly who showed interest. No more guessing if an employer pulled your resume, you'll have a receipt.

The Form

Elements we will want to capture are name, email, company, and the format of the resume they wish to receive. All fields will be required to successfully submit the form. Let's code the form using ASP.NET Web Form controls and attach validation controls.
<div id="requestResume" runat="server"> 
<!-- The values on this FORM will be posted back to the server. --> 
<!-- We will add the 'runat="server"' to the FORM and controls --> 
<form id="Form1" method="post" runat="server"> 
<table> 
<tr><td>Name</td>
<td><input type="text" id="txtName" 
value="" size="30" maxlength="50" 
runat="server" name="txtName"/> 
<!-- txtName is required. We will attach a 
RequiredFieldValidator by assigning 
txtName as the ControlToValidate --> 
<asp:RequiredFieldValidator id="valRequiredName" 
runat="server"     
ControlToValidate="txtName"     
ErrorMessage="* You must enter your Full Name."
     Display="dynamic".>
</td></tr> 
<tr><td>Email</td>
<td> <input type="text" id="txtEmail" 
value="" size="30" maxlength="50" 
runat="server" NAME="txtEmail"/> 
<!-- txtEmail is both required and must be a valid email address. -->
<asp:RequiredFieldValidator id="valRequiredEmail" 
runat="server"     
ControlToValidate="txtEmail"     
ErrorMessage="* You must enter your Email address."     
Display="dynamic"/>
<!-- txtEmail will be validated using Regular Expression. 
It is also attached to txtEmail via the ControlToValidate property. --> 
<asp:RegularExpressionValidator id="valValidEmail" 
runat="server"     
ControlToValidate="txtEmail"     
ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"     
ErrorMessage="* You must enter a valid Email address"     
Display="dynamic"/> 
</td></tr> 
<tr><td>Company</td>
<td> <!-- txtCompany works exactly like txtName. --> 
<input type="text" id="txtCompany" value="" size="30" 
maxlength="50" runat="server" 
NAME="txtCompany"/> 
<asp:RequiredFieldValidator id="valRequiredCompany" 
runat="server"     
ControlToValidate="txtCompany"     
ErrorMessage="* You must enter your Company name."     
Display="dynamic"/>
</td></tr> 
<tr><td>Resume Format</td>
<td> 
<!-- The dropdown list of resume formats 
will be assigned on the server --> 
<asp:DropDownList id="selResume" Runat="server"/>
</td></tr> 
<tr>
<td></td>
<td> 
<!-- When the FORM is submitted it will 
execute the "btnSubmit_OnClick" 
Subroutine on the server. --> 
<asp:button type="submit" name="btnSubmit" 
onclick="btnSubmit_OnClick" 
text="Request Resume" 
runat="server"/> </td></tr> 
</table> 
</form> 
</div>

Server-Side Code (VB.NET)

Regardless of what language you utilize, the above FORM will be coded the same way. To proceed on the server-side code, we need to pick a language. C# and Visual Basic are the two most popular at this time. Let's proceed in VB. The first line imports the ASP.NET code needed to perform sending email.
<%@ Import Namespace="System.Web.Mail" %> 
<script language="VB" runat="server">     
Sub Page_Load()         
'=== When the page first loads populate the Resume Format dropdown         
If NOT IsPostBack() Then            
   Call populateResumeFormats()            
   selResume.DataValueField = "Value"            
   selResume.DataTextField = "Key"            
   selResume.DataBind()         
End If     
End Sub     

Public Sub populateResumeFormats         
'=== create a HashTable to populate the dropdown.         
   Dim dropResume As New HashTable(4)         
   dropResume.Add("Word 97 (.RTF)", "resume.rtf")         
   dropResume.Add("HTML", "resume.htm")         
   dropResume.Add("Word 2002", "resume.doc")         
   dropResume.Add("Text", "resume.txt")         
   selResume.DataSource = dropResume     
End Sub     

Sub btnSubmit_OnClick(o as Object, e as EventArgs)         
'=== The Page.IsValid call checks all the validation controls. 
'=== If they all pass then the form is valid.         
If Page.IsValid Then             
'=== no longer display the FORM, display a status message. 
   requestResume.InnerHTML = "You will receive an email shortly with my resume.
 Thank you for your interest."             
   Call sendResume         
End If     
End Sub     

Sub sendResume        
'=== create a MailMessage         
  Dim resumeEmail as New MailMessage         
  Dim strResumeFilePath as String         
'=== To, CC, BCC, From, Subject are self-explainatory         
  resumeEmail.To = txtEmail.value
  resumeEmail.BCC = "larryking@cnn.com"         
  resumeEmail.From = "larryking@cnn.com"         
  resumeEmail.Subject = "Resume for Larry King"         
  resumeEmail.BodyFormat = MailFormat.text         
  resumeEmail.Body = txtName.value & ", attached is a copy of my resume. 
I look forward to working with you. - MAS "         
'=== for this example all versions of the resume reside in the 
/resume directory           
  strResumeFilePath = Server.MapPath("/") & "\resume\" & selResume.SelectedItem.value         
'=== create an attachment and add the file path to that attachment         
  Dim resumeAttachment as New MailAttachment(strResumeFilePath)            
  resumeEmail.Attachments.Add(resumeAttachment)         
'=== send the email         
  SmtpMail.Send(resumeEmail)     
End Sub </script>

Last Words

Pretty straightforward isn't it? Now you have a working resume mailer. The employer can specify the preferred format, and the potential employee gets to track who is viewing the resume.

This article was written in 2001 for ASP.NET 1.0. Forgive the non XHTML.

Labels: , ,

 

Print Friendly CSS Stylesheet

Back in the day developers often created a separate web page to host the printer friendly version of content. The print friendly page stripped the large headers, footers, side bars and usually advertising. Sometimes the font would be changed from a screen readable font to a paper readable font. Using CSS we can accomplish these goals without even having to do a page refresh. If you are unclear so far, do a Print Preview on this page. You'll see the header is gone. The side bar has been removed. And the content now fills the width of the screen, which means less pages to pick up at the printer. I could go further with stylesheet by changing all the font colors to black to save your color ink cartridges.

Print Friendly Overview

Create a page that is XHTML compliant. Place each section (header, footer, content, sidebar, etc) in it's own DIV with a unique ID. Now hide the sections you don't want to go to the printer using display: none;. You'll probably hide every section except the content, although you may wish to keep the footer for copyright purposes. The last step is to perform minor tweaks to get margins how you like and any color adjustments. Upload the CSS file and add the reference using a media="print" attribute.
<link rel="stylesheet" media="print" type="text/css" href="print.css"/>

Labels:

 

SQL Injection - Case Study

Well I got nailed today. My site INeedCoffee.com which is written using Classic ASP fell victim to a SQL Injection attack. The damage was limited to just one column in a table of nine rows.

Textbook SQL Injection Attack

Almost every example I've seen that explains SQL Injection shows how the WHERE clause is vulnerable to querystring manipulation.
sAuthorID = Request.QueryString("AuthorID")
sSQL = "SELECT firstName, lastName FROM Author WHERE authorID = " & sAuthorID

This query can be nailed with a single quote and an OR clause to dump the entire table to the screen. Adding 1' or '1'='1 to the querystring now yields this SQL.
SELECT firstName, lastName FROM Author WHERE authorID = 1 OR 1=1

Attacked Via a Column Sort

The attack I received came from appending additional SQL to ORDER BY clause. By clicking on the column header on the contributor page a column number is passed to the querystring. This was getting concatenated to a SQL statement.

EXAMPLE: http://ineedcoffee.com/by/michael_allen_smith/?s=1

The server-side ASP code looked like this:
sort = Request.QueryString("s")
If Len(sort) Then
    sSQL = sSQL & " ORDER BY " & sort
Else
    sSQL = sSQL & " ORDER BY C.Created DESC " 
End If

The hacker guessed the name of the table and one of the column names. From there it was an easy to hack together a querystring that looked like this:

http://ineedcoffee.com/by/michael_allen_smith/?s=1;UPDATE Section SET Name='hacked'

A simple semicolon followed by an UPDATE statement. This was just one column on a minor lookup table. I'm grateful that he/she didn't use the DROP TABLE command in the right sequence.

Locking It Down

Although ASP.NET has a nice feature to add parameters to dynamic SQL, Classic ASP is still best protected with stored procedures. Now the sort parameter goes into a stored procedure which can only perform SELECT. Prior to entering the stored procedure I'm now performing tests on length of the querystring (LEN) and determining if is an integer (IsNumeric).

Labels: , ,

 

Using Yahoo! Maps GeoCoding API in C#

Building a map using Yahoo! Maps or Google Maps requires Latitude and Longitude points. Until Yahoo! released it's GeoCoding APIs getting address latitude and longitude was neither easy or free. At this time Yahoo! allows you to GeoCode 50,000 addresses a day. The code below will call the Yahoo! GeoCoding API using C# and ASP.NET 2.0. Yahoo!

Latitude and Longitude Precision

When supplying Yahoo! with an address, it will try to return the highest level of precision. The returned XML document will tell you how precise the latitude and longitude are. If it can't resolve an address, it will return a warning. Yahoo! Map

Files Included in Download

* GeoCode.aspx - Form to enter address. Also will display response from Yahoo!.
* GeoCode.aspx.cs - Calls class to make geocoding request.
* GeoAddress.cs - An address class.
* GeoAddressAPI.cs - Class which calls Yahoo! and parses response.
* web.config - Holds application ID required by Yahoo!

GeoCode.zip - Don't forget to enter your application ID at the end of the querystring in the web.config file. This format supports ASP.NET 2.0. If you are running 1.1, you will need to make some minor adjustments to how you store your API url in the web.config file.

Working Demo

GeoCoding C# Yahoo Demo

Labels: , , ,

 

Detecting HTTP vs HTTPS in ASP.NET

One simple command will let you know if the user is accessing your page via SSL.
Request.IsSecureConnection

Example

if (Request.IsSecureConnection)
{
    Response.Write("HTTPS");
}
else
{
    Response.Write("HTTP");
}

Redirecting to HTTPS

if (!Request.IsSecureConnection)
{
    // send user to SSL 
    string serverName =HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);        
    string filePath = Request.FilePath;
    Response.Redirect("https://" + serverName + filePath);
}
HttpRequest.IsSecureConnection Property (MSDN)

Labels:

 

Blogger Label List for FTP Accounts (ASP.NET)

The new version of Blogger includes tags. They call them Labels. Hosted accounts (BlogSpot) get templates which display a list of tags for quick navigation. This feature is not available to FTP accounts. This post is a hack to create a user control in ASP.NET that displays a Label List. Before I do, lets breakdown how labels look on the file system.
  1. Labels reside in a folder. By default that folder is called labels.
  2. Every label gets one file.
  3. The label file name is the same as the label name plus extension.
  4. Inside each file is a copy of each blog with that label.
  5. Blogger places the class name of blogger-labels before the Labels text.
For example, on this site I have a label called Javascript.
http://digitalcolony.com/labels/Javascript.aspx
It resides in the labels folder just outside the blog root. The name of the label is Javascript. The name of the file is Javascript.aspx. As of this writing there is 1 post and therefore 1 instance of blogger-labels in the View Source. With the above facts laid out, this is an overview of how to create your own Label List control.
  1. Using server-side code to handle Files and Directories, open the labels folder.
  2. Collect each file name (minus the file extension).
  3. Open each file and count how many times blogger -labels appears
  4. Write label and count to screen.
Here is my .NET code for the Blogger Label List control. On the .ASPX page that the control will be on add this line to the top to register the control.
<%@ Register Src="~/LabelList.ascx" TagName="LabelList" TagPrefix="dc" %>
And the control on the page gets placed with this line.
<dc:LabelList ID="LabelList1" runat="server" Folder="labels" DisplayCount="true" />
And here is the code for the LabelList.ascx user control.
using System;
using System.Web;
using System.IO;
using System.Text;

public partial class LabelList : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {       
        string rootDir = Server.MapPath("~");
        string labelDirectory = rootDir + folder;
        StringBuilder sb = new StringBuilder();
        if(Directory.Exists(labelDirectory)){
            
            string[] fileList = Directory.GetFiles(labelDirectory,"*.aspx");
            string labelName;
            string labelCount;

            foreach (string file in fileList)
            {
                FileInfo fi = new FileInfo(file);
                labelName = fi.Name.Replace(fi.Extension, "");

                if (displayCount)
                {
                    StreamReader sr = new StreamReader(fi.FullName);
                    string webPage = sr.ReadToEnd();
                    sr.Close();
                    int webPageLen = webPage.Length;
                    webPage = webPage.Replace("bl0gger-labels", "bl0gger-labelsX");
                    int iLabelCount = webPage.Length - webPageLen;
                    labelCount = "(" + iLabelCount.ToString() + ")";
                }
                else
                {
                    labelCount = "";
                }
                folder = folder.Replace("\\","/");
                if (folder.Substring(folder.Length - 1, 1) == "/")
                    folder = folder.Substring(0, folder.Length - 1);
                sb.Append("<a href=\"" + folder + "/" + fi.Name + "\">" + 
labelName + labelCount + "</a><br/>");               
            }            
        } 
        else {
            sb.Append("No labels in folder [" + labelDirectory + "] defined.");
        }
        Response.Write(sb.ToString());
    }

    private string folder;
    public string Folder
    {
        get { return folder; }
        set {
            if (value.Substring(0, 1) == "/")
                folder = value;
            else
                folder = "/" + value;
            folder = folder.Replace("/","\\");
        }
    }
    private bool displayCount;
    public bool DisplayCount
    {
        get { return displayCount; }
        set { displayCount = value; }
    }
}
Note that the code above misspells blogger-labels as to not confuse the count of the Label control that I'm using for this site. If you cut and paste the code, fix that spelling.

PHP and ASP coders should be able to hack up something similar using the above code as a framework.

UPDATE (Feb 2007): I created a version of Blogger Label Lists using Classic ASP.

Labels: ,

 

Tips to Create a Javascript Directory or Newsfeed

One of the developer newsletters I received a while back directed me to the DevASP site. On that site I spied something wonderful. They had created a directory of their site with 1 line of Javascript code, available to any web site. Although I had no need to use their directory, I had to create one of these for INeedCoffee and DeepFitness. It was essential that I figured out how this magic took place. Getting ASP to write valid Javascript was easy enough, figuring out which folder the user clicked on wasn't. After playing with code for a few hours, I decided to contact DevASP. After all, they are supposedly a site dedicated to helping developers. Did they help me? No. They didn't even return my email. So much for helping the developer. After getting shunned by DevASP, my goal was to figure it out, perfect it, and then make the code available here on DigitalColony.

It took quite a bit of research as well as trial and error, but I figured it out. My hope is after you read this you won't spend as much time as I did creating your Javascript directory.

The DeepFitness Newsfeed

Before we proceed, here is the Javascript newsfeed that was created for DeepFitness.
<script language="JavaScript" 
src="http://www.deepfitness.com/togo.aspx" 
type="text/javascript"></script>

Design Backwards

My biggest mistake completing this project was jumping straight into the server-side code. In order for this project to work, you need to have valid HTML generated by valid Javascript, produced by bug-free server-side code. If you jump into the server-side code and get a bug, you won't see it on the screen. The View Source will only yield you that single line of javascript code, which doesn't make debugging an easy task. You can minimize these problems by mocking up the HTML first. Once you've completed the mockup, test your HTML with different DOCTYPE values. Since your javascript directory or newsfeed will reside on many different pages with potentially different DOCTYPE values, it's important that your HTML looks good in each scenerio. Note: unless you do a pure CSS output, your code probably won't validate under each DOCTYPE case. That's OK. If the host of your directory wanted W3C approval, they would be importing your RSS feed and styling it themselves. Breaking Down the Steps

Here is a quick overview of what takes place after an HTML page is loaded that points to the DeepFitness directory generator (www.deepfitness.com/togoDir.aspx).

  1. The .ASPX page will use Response.Write to output Javascript's document.write. Inside the document.write will be valid HTML.
  2. When the DeepFitness directory loads, the code will check the HTTP Referral. In classic ASP this is done with a Request.ServerVariable("http_referer"). In .NET, it is retrieved with a Request.UrlReferrer.
  3. Inside the UrlReferrer there may be a querystring telling the code which folder has been requested. If there is no querystring, that tells the code to load the root. If there is a querystring, our next step is to parse it and retrieve the folderID being requested.
  4. Once you have the folderID, then comes the easy stuff. Using that you can get a list of folders and links that match that folder and provide a link back to the home page. This part of the task will depend upon your database structure and what data your wish to represent.

Potential Problems

Above I described how the DOCTYPE on the host page can potentially change the look of your outputted javascript. That can easily be avoided by testing mockups prior to starting your server-side coding. A more serious problem is the prevelance of software tools that block the HTTP referral. Programs like Norton Internet Security and the browser Opera have options to disguise what page the user is coming from. In order for a nested directory application to work, we need to know this information. But if we can't have access to it, then they can't have access to our directory. The DeepFitness directory will hide the folders and just display the Latest Updates if the user blocks their HTTP Referral. If we didn't hide the folders, those users would click onto folders only to be returned back to the root everytime. Not exactly a friendly user interface. Prior to coding, define the behavior your directory should take if it you don't have access to the user's HTTP Referral.

Formatting For Javascript

Certain characters in your data will cause Javascript to crash. Special characters need to be handled with your server-side code. Below is a C# function that I create to do just such task.

EX: FormatForJS(myDataReader["currentBranch"].ToString());

Converting this to Classic ASP should be easy enough

protected string FormatForJS(object input) { 
  string data = input.ToString(); 
  // cast the input to a string 
  data = data.Trim(); 
  // replace those characters that will crash JAVASCRIPT 
  data = data.Replace("'", "\\'"); 
  data = data.Replace("\n", ""); 
  data = data.Replace("\r", ""); 
  return data; 
}

Prevent Caching

Users will often use the back button on their browsers to navigate back up your directory. Unless we do something in our code, the directory will not refresh and the user will remain on the same folder. In .NET the way we force a refresh is with this command: Response.Cache.SetNoStore(); By doing this, the code is never cached and the back button will behave the same as our back link.

Speed Improvements

You can create a Javascript directory using any server-side language. They should all do a fine job, however if you expect a lot of web sites to host it, you'll want it to run as fast as possible. One option is to use compiled code (.NET) instead of scripting code (Classic ASP). And to get the database portion smoking fast, compile your SQL into views and/or stored procedures.

This article was originally written in 2004.

Labels: ,

 

SortedList Example for ASP.NET in C#

The SortedList object uses a key/value combination to sort a list. The SortedList can be databound to the following ASP.NET controls:
  • asp:RadioButtonList
  • asp:CheckBoxList
  • asp:DropDownList
  • asp:ListBox
Our examples will bind a SortedList to an asp:ListBox.
<asp:ListBox ID="lsbPresidents" runat="server" />

Numeric Sort

SortedList s = new SortedList();
s.Add(16, "Lincoln");
s.Add(42, "Clinton");
s.Add(40, "Reagan");
s.Add(1, "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Washington
Lincoln
Reagan
Clinton      

Alphabetic

SortedList s = new SortedList();
s.Add("L", "Lincoln");
s.Add("C", "Clinton");
s.Add("R", "Reagan");
s.Add("W", "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Clinton   
Lincoln  
Reagan 
Washington

Date Sort

SortedList s = new SortedList();
s.Add(Convert.ToDateTime("2/12/1809"), "Lincoln");
s.Add(Convert.ToDateTime("8/19/1946"), "Clinton");
s.Add(Convert.ToDateTime("2/6/1911"), "Reagan");
s.Add(Convert.ToDateTime("2/22/1732"), "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Washington
Lincoln
Reagan
Clinton      

Labels:

 

Sending Email Using CDONTS

ASP developers can use the CDONTS technology, which is built into Microsoft's IIS web server to send server-side email. CDONTS, which is also referred to as CDO, stands for Collaboration Data Objects. Once an SMTP service is setup, CDONTS allows the sending of email from ASP script. Setting up an SMTP service is outside the scope of this article.

Basic Text Email

The simplest email is the plain text message. Below, is sample code for sending a plain text email. The following example will send a message to Huey, carbon-copy Louie, and blind-carbon-copy Dewey.
on error resume next 
Set Mailer = Server.CreateObject("CDONTS.NewMail") 
Mailer.From = "Duck Newsletter<newsletter@duck.com>" 
Mailer.To = "huey@duck.com" 
Mailer.CC = "louie@duck.com" 
Mailer.BCC= "dewey@duck.com" 
Mailer.Subject = "Duck Newsletter" 
mailBody = "-- newsletter text --" 
Mailer.Body = mailBody 
Mailer.Send 
If err.num Then    
    Response.Write "CDONTS Error: " & err.num & " - " & err.description
End If

Wrapping it up with HTML

Sending HTML email is just another variation on text email, since HTML is merely marked up text. Other than the HTML tags, the only difference is specifying a valid DOCTYPE in the first line of the email. There a few valid DOCTYPES to chose from when constructing an HTML email. Web Design Group has put out a good article that describes the differences: Choosing a DOCTYPE. It also advised to include a META tag describing the Content-Type. In this example the Content-Type is Latin1 HTML.
on error resume next 
Set Mailer = Server.CreateObject("CDONTS.NewMail") 
Mailer.From= "Duck Newsletter<newsletter@duck.com>" 
Mailer.To = "huey@duck.com" 
Mailer.Subject = "Duck Newsletter" 
' BodyFormat and MailFormat should be set to 0 for HTML emails. 
Mailer.MailFormat = 0 
Mailer.BodyFormat = 0 
Dim mailBody
' 1-add DOCTYPE to mailBody
' 2-add HTML HEAD TITLE and BODY tags to mailBody
' 3-construct message using valid HTML inside mailBody
' 4-close BODY and HTML tags to mailBody
Mailer.Body = mailBody 
Mailer.Send 
If err.num Then    
     Response.Write "CDONTS Error: " & err.num & " - " & err.description 
End If

More CDONTS Features

You may want to use CDONTS for two other things: sending a file attachment and assigning a message priority. You can also provide an optional 2nd parameter to name the file attachment. Defining priority is done via the Importance property. The three posible values are: High (2), Normal (1), or Low (0). The default is Normal. This example will send a file attachment and mark the message as HIGH priority.

Last Words

CDONTS is a very robust way to send server-side email. I've used it to send 1000 newsletters on 1 ASP page without error. If you plan to use CDONTS to send a large number of newsletters from a single page, be sure to add "Server.ScriptTimeout = 99999999" at the top of the ASP page. This will prevent IIS from timing out the page while the messages are being sent.

This article was originally written in 2001.

Labels: ,

 

Digital Colony Copyright © 1999-2008 XHTML   508
This site uses Blogger, which is not 100% XHTML compliant.
Try...Catch Disclaimer: For brevity many examples do not include error handling. That is your responsibility.