Praveen's Blog

An Eternal Quest for Incremental Improvement

AT&T Store Apple iPhone 3G preorder status checking

I have pre-ordered Apple iPhone 3G from my neighboring AT&T store last Sunday (Jul 20). I have done this as purchasing from the Apple Store wouldn't let me to apply my FAN discounts on the plan. Applying it latter would reflect only after a couple of billing cycles. Also I don't wanna get into the crazy line. I didn't join the true Apple cult yet. I was just curious about the status of the pre-order and I checked the status online and found that the status query mechanism can be exploited to post automated queries. Here is a small ruby script that I wrote to check bulk statuses. This would also give you a clue on where you are in preorder line.

This script is intended to be used to check your order status only. Please note that this comes with absolutely no warranty and I can't be held responsible for the misuse of the script. AT&T may change their query mechanism if they know about this exploit.

#!/usr/bin/env ruby

require 'rubygems'
require 'open-uri'
require 'openssl'

# FIXME (2008-07-24, praveen): This is nasty. I don't know of any other
# simpler way to skip SSL certificate verification.

class Order
  attr_reader :order_number, :zip_code, :valid, :canceled, \
  :customer_name, :order_date, :shipped, :ship_date, :ship_carrier, \

  def initialize order_number, zip_code
    @order_number = order_number
    @zip_code = zip_code
    @valid = false
    @canceled = false

    order_status_uri = URI.parse "\
    order_status =

    if !order_status.scan("We're sorry, but the information you entered \
was not recognized by our systems.").to_s.empty?
      @valid = false
    @valid = true

    if !order_status.scan("Canceled")[0].to_s.empty?
      @canceled = true
    @canceled = false

    @customer_name =
    @order_date =
      order_status.scan(/Date Ordered:.*/).to_s.gsub!(/Date Ordered:\s*/, "")
    @order_date.gsub!(/\s/, "")
    @shipped =
      order_status.scan(/\<td width="5%".*/)[3].scan(/[01]/).to_s.to_i == 1 ?
    true : false
    if !@shipped
    @ship_date = order_status.scan(/\<td width="6%".*/)[5]
    @ship_date.gsub! /^.*\<p\>/, ""
    @ship_date.gsub! /\<\/p\>.*$/, ""
    @ship_carrier = order_status.scan(/\<td width="6%".*/)[7]
    @ship_carrier.gsub! /^.*\<p\>/, ""
    @ship_carrier.gsub! /\<\/p\>.*$/, ""
    @tracking_number =
    @tracking_number.gsub! /\http:\/\/\/Tracking\?tracknumbers=/, ""

  def print
    if !@valid
      puts "#{@order_number.ljust(6)} ------------------------ NOT FOUND \
    if @canceled
      puts "#{@order_number.ljust(6)} ------------------------ CANCELLED \
    if @shipped
      puts "#{@order_number.ljust(6)} #{@customer_name.slice(0..14).ljust(15)} \
#{@order_date} #{'Yes'.ljust(5)} #{@ship_date} #{@ship_carrier.ljust(6)} \
      puts "#{@order_number.ljust(6)} #{@customer_name.slice(0..14).ljust(15)} \
#{@order_date} #{'No'.ljust(5)} #{'NA'.ljust(8)} #{'NA'.ljust(6)} \

if ARGV.length != 3
  puts "Usage: ./order-status.rb ZIP BEGIN END

  ZIP      ZIP code of the AT&T store where the order is placed.
  BEGIN    Order number to begin searching for.
  END      Order number to end searching for.

Example: ./order-status.rb 94025 12000 12100
  puts "Que Order# #{'Name'.ljust(15)} Order Dt Ship? Ship Dt  Carrie Tracking"
  puts "----------------------------------------------------------------------"
  queue = 0
  for order_number in ARGV[1]..ARGV[2]
    o = order_number, ARGV[0]
    if o.valid and !o.canceled
      queue += 1 if !o.shipped
      display_queue = o.shipped ? "NA" : queue
      display_queue = "NA"
    print "#{display_queue.to_s.ljust(3)} "

Sample Output

Data is scrambled to protect identity.

praveen@athena:~/scratch/ruby$ ./order-status.rb [ZIP] XX208 XX250
./order-status.rb:9: warning: already initialized constant VERIFY_PEER
Que Order# Name            Order Dt Ship? Ship Dt  Carrie Tracking
NA  XX208  ***IVEL         07/15/08 Yes   07/18/08 F      981870058XXX
1   XX209  ***EPHINE       07/15/08 No    NA       NA     NA
2   XX210  ***PBELL        07/15/08 No    NA       NA     NA
3   XX211  ***ISSA         07/15/08 No    NA       NA     NA
NA  XX212  ------------------------ NOT FOUND ------------------------
NA  XX213  ***N            07/15/08 Yes   07/18/08 F      981870063XXX
NA  XX214  ***E            07/15/08 Yes   07/18/08 FDE11  978403293XXX
4   XX215  ***DIRI         07/15/08 No    NA       NA     NA
NA  XX216  ***TT           07/15/08 Yes   07/24/08 F      981871193XXX
5   XX217  ***E            07/15/08 No    NA       NA     NA
6   XX218  ***TT           07/15/08 No    NA       NA     NA
7   XX219  ***ESH          07/15/08 No    NA       NA     NA
8   XX220  ***D            07/15/08 No    NA       NA     NA
9   XX221  ***ECCA         07/15/08 No    NA       NA     NA
10  XX222  ***ENIA         07/15/08 No    NA       NA     NA
NA  XX223  ***TEH          07/15/08 Yes   07/18/08 F      981870054XXX
11  XX224  ***TER          07/15/08 No    NA       NA     NA
NA  XX225  ***TER          07/15/08 Yes   07/24/08 F      981847797XXX
NA  XX226  ------------------------ CANCELLED ------------------------
NA  XX227  ***DA           07/16/08 Yes   07/24/08 FDE51  982002804XXX
NA  XX228  ------------------------ CANCELLED ------------------------
NA  XX229  ***HEESH        07/16/08 Yes   07/18/08 F      981846745XXX
12  XX230  ***ER           07/16/08 No    NA       NA     NA
13  XX231  ***A            07/16/08 No    NA       NA     NA
14  XX232  ***EL           07/16/08 No    NA       NA     NA
15  XX233  ***SWORTH       07/16/08 No    NA       NA     NA
16  XX234  ***SWORTH       07/16/08 No    NA       NA     NA
17  XX235  ***SWORTH       07/16/08 No    NA       NA     NA
18  XX236  ***RGE          07/16/08 No    NA       NA     NA
19  XX237  ***AB           07/16/08 No    NA       NA     NA
20  XX238  ***QUIEL        07/16/08 No    NA       NA     NA
NA  XX239  ***RY           07/16/08 Yes   07/18/08 F      981870059XXX

Credit: The credit also goes to an anonymous guy who wrote a similar script that I came across in a forum (I forgot the exact place). Most of the regexp ideas were shamelessly stolen from his original script. This is my first non-trivial Ruby script. I don't know much about Ruby. This was a learning script as well. Please bear with newbie mistakes and coding conventions. If you have any comments on the code, I will be more than happy to hear from you.

Update (2008-07-29): AT&T finally decided to do something about the vulnerability. They are using "Captcha" now. So, this script might not work anymore. However I am pretty close to the head of the queue. I might get my phone in a couple of days or so. So, I don't worry about this anymore...

Update (2008-08-02): My phone shipped today.