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. OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE class Order attr_reader :order_number, :zip_code, :valid, :canceled, \ :customer_name, :order_date, :shipped, :ship_date, :ship_carrier, \ :tracking_number def initialize order_number, zip_code @order_number = order_number @zip_code = zip_code @valid = false @canceled = false order_status_uri = URI.parse "https://www.wireless.att.com/order_status/\ order_status_results.jsp?fromwhere=order_status&vMethod=ordernum&\ vNumber=#{order_number}&ZipCode=#{zip_code}&x=40&y=14" order_status = order_status_uri.read if !order_status.scan("We're sorry, but the information you entered \ was not recognized by our systems.").to_s.empty? @valid = false return end @valid = true if !order_status.scan("Canceled")[0].to_s.empty? @canceled = true return end @canceled = false @customer_name = order_status.scan(/Customer:\s*\w*/).to_s.gsub!(/Customer:\s*/,"") @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 return end @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 = order_status.scan(/\http:\/\/fedex.com\/Tracking\?tracknumbers=\d*/).to_s @tracking_number.gsub! /\http:\/\/fedex.com\/Tracking\?tracknumbers=/, "" end def print if !@valid puts "#{@order_number.ljust(6)} ------------------------ NOT FOUND \ ------------------------" return end if @canceled puts "#{@order_number.ljust(6)} ------------------------ CANCELLED \ ------------------------" return end 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)} \ #{@tracking_number}" else puts "#{@order_number.ljust(6)} #{@customer_name.slice(0..14).ljust(15)} \ #{@order_date} #{'No'.ljust(5)} #{'NA'.ljust(8)} #{'NA'.ljust(6)} \ #{'NA'.ljust(12)}" end end end 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 " else 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.new order_number, ARGV[0] if o.valid and !o.canceled queue += 1 if !o.shipped display_queue = o.shipped ? "NA" : queue else display_queue = "NA" end print "#{display_queue.to_s.ljust(3)} " o.print end end
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
PS: 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.


Recent Comments