File Coverage

blib/lib/WWW/Ebay/Session.pm
Criterion Covered Total %
statement 69 554 12.4
branch 0 190 0.0
condition 0 62 0.0
subroutine 23 45 51.1
pod 14 14 100.0
total 106 865 12.2


line stmt bran cond sub pod time code
1              
2             =head1 COPYRIGHT
3              
4             Copyright (C) 2002-present Martin Thurn
5             All Rights Reserved
6              
7             =head1 NAME
8              
9             WWW::Ebay::Session - log in to eBay and access account information
10              
11             =head1 SYNOPSIS
12              
13             use WWW::Ebay::Session;
14             my $oSession = new WWW::Ebay::Session('ebay-userid', 'ebay-password');
15              
16             =head1 DESCRIPTION
17              
18             Allows you to programatically log in as a particular user and fetch
19             webpages from the eBay auction website (www.ebay.com).
20              
21             =head1 AUTHOR
22              
23             Martin 'Kingpin' Thurn, C, L.
24              
25             =head1 METHODS
26              
27             =over
28              
29             =cut
30              
31             package WWW::Ebay::Session;
32              
33 3     3   5762 use strict;
  3         5  
  3         79  
34 3     3   10 use warnings;
  3         5  
  3         96  
35              
36             require 5.006;
37              
38 3     3   10 use Data::Dumper; # for debugging only
  3         3  
  3         122  
39 3     3   350 use Date::Manip;
  3         95182  
  3         969  
40 3     3   430 use File::Spec::Functions;
  3         505  
  3         249  
41 3     3   1626 use HTML::Form;
  3         17611  
  3         92  
42 3     3   1356 use HTML::TreeBuilder;
  3         21058  
  3         27  
43 3     3   645 use HTTP::Cookies;
  3         7772  
  3         92  
44 3     3   1667 use HTTP::Request::Common qw( GET POST );
  3         13071  
  3         187  
45 3     3   370 use LWP::Simple;
  3         16690  
  3         20  
46 3     3   897 use LWP::UserAgent;
  3         4  
  3         50  
47 3     3   1123 use WWW::Ebay::Listing;
  3         5  
  3         83  
48 3     3   536 use WWW::Search;
  3         28837  
  3         130  
49             # We need the version whose _parse_enddate() takes a string as arg2:
50 3     3   492 use WWW::Search::Ebay 2.181;
  3         6505  
  3         104  
51             # We need the version that has the shipping() method:
52 3     3   12 use WWW::SearchResult 2.070;
  3         32  
  3         53  
53              
54 3     3   9 use constant DEBUG_EMAIL => 0;
  3         5  
  3         133  
55 3     3   9 use constant DEBUG_FETCH => 0;
  3         4  
  3         101  
56 3     3   9 use constant DEBUG_FUNC => 0;
  3         2  
  3         101  
57 3     3   9 use constant DEBUG_SELLING => 0;
  3         4  
  3         106  
58 3     3   9 use constant DEBUG_SOLD => 0;
  3         3  
  3         91  
59 3     3   14 use constant DEBUG_UNSOLD => 0;
  3         3  
  3         90  
60 3     3   8 use constant DEBUG_WATCH => 0;
  3         2  
  3         107  
61 3     3   8 use constant DEBUG_READ_LOCAL_FILES => 0;
  3         5  
  3         13271  
62              
63             our
64             $VERSION = 1.65;
65              
66             sub _debug
67             {
68             # return unless $iDEBUGGING;
69 0     0     print STDERR @_;
70             } # _debug
71              
72             =item new
73              
74             Creates a new object of this type.
75              
76             =cut
77              
78             sub new
79             {
80 0     0 1   my $class = shift;
81             # This is NOT a clone method:
82 0 0         return undef if ref $class;
83 0           my ($sUserID, $sPassword) = @_;
84 0           my $self = {
85             # Create cookie jar and UserAgent not now, but only when
86             # needed:
87             '_cookie_jar' => undef,
88             '_user_agent' => undef,
89             '_error' => '',
90             '_pass' => $sPassword,
91             '_user' => $sUserID,
92             '_response' => undef,
93             '_selling_page' => '',
94             'raoSold' => undef,
95             'raoSelling' => undef,
96             'raoWatching' => undef,
97             };
98 0           bless ($self, $class);
99 0           return $self;
100             } # new
101              
102              
103             =item response
104              
105             Returns the HTTP::Response object that resulted from the most recent page fetched.
106              
107             =cut
108              
109             sub response
110             {
111 0     0 1   my $self = shift;
112 0 0         if (@_)
113             {
114 0           $self->{_response} = shift;
115             } # if
116 0           return $self->{_response};
117             } # response
118              
119             =item signin
120              
121             This method can be called if you only need the encrypted password.
122              
123             =cut
124              
125             my %hssPasswords;
126              
127             sub signin
128             {
129 0     0 1   my $self = shift;
130 0   0       my $sUserID = $self->{_user} || '';
131 0   0       my $sPassword = $self->{_pass} || '';
132 0           DEBUG_FUNC && print STDERR " DDD Ebay::Session::signin($sUserID)\n";
133 0           print STDERR " DDD signin($sUserID,$sPassword)\n" if DEBUG_FETCH;
134 0 0         if (! exists($hssPasswords{$sUserID}))
135             {
136             # Get the sign-in page and parse it:
137 0           print STDERR " DDD fetching ebay sign-in page...\n" if DEBUG_FETCH;
138             # my $sPage = $self->fetch_any_ebay_page('http://cgi.ebay.com/aw-cgi/eBayISAPI.dll?SignIn', 'signin', 'ignore-refresh');
139 0           my $sPage = $self->fetch_any_ebay_page('http://signin.ebay.com/ws/eBayISAPI.dll?SignIn&ssPageName=h:h:sin:US', 'signin', 'ignore-refresh');
140             # http://signin.ebay.com/ws/eBayISAPI.dll?SignIn&ssPageName=h:h:sin:US&ru=http%3A//my.ebay.com/ws/ebayISAPI.dll%3FMyeBay%26CurrentPage%3DMyeBayAllSelling
141             # NEW: No encrypted password sent, only cookies. See if the
142             # sign-in succeeded:
143 0 0         $hssPasswords{$sUserID} = ($sPage =~ m!If you are seeing this page,!i) ? 1 : 'FAILED';
144             # OLD: Grab a copy of the encrypted password:
145             # $hssPasswords{$sUserID} = ($sPage =~ m!(&|;)pass=(.+?)&!) ? $2 : 'FAILED';
146             } # if
147 0           return $hssPasswords{$sUserID};
148             } # signin
149              
150              
151             =item fetch_any_ebay_page
152              
153             =cut
154              
155             sub fetch_any_ebay_page
156             {
157 0     0 1   my $self = shift;
158             # Required arg1 == HTTP::Request object, or URL as string:
159 0           my $oReq = shift;
160             # Optional arg2 == name of this page (for debugging msgs):
161 0   0       my $sName = shift() || '';
162             # Optional arg3 == whether to ignore meta-refresh tags (default is
163             # to follow redirects):
164 0   0       my $iIgnoreRefresh = shift() || 0;
165 0           DEBUG_FUNC && print STDERR " DDD Ebay::Session::fetch_any($sName)\n";
166 0           my $fname = "Pages/$sName.html";
167 0           my $sPage = '';
168 0           if (DEBUG_READ_LOCAL_FILES && ($sName ne '') && -f $fname)
169             {
170             unless (open DBG, "<$fname")
171             {
172             print STDERR " --- DEBUG_READ_LOCAL_FILES is on, but can not open $fname for read: $!\n";
173             return '';
174             } # unless
175             local $/ = undef; # slurp entire file
176             $sPage = ;
177             close DBG;
178             return $sPage;
179             } # if
180 0           print STDERR " DDD in fetch_any_ebay_page, oReq is $oReq\n" if DEBUG_FETCH;
181 0           my $ref = ref $oReq;
182 0 0 0       unless ((defined $ref) && ($ref =~ m!HTTP::!))
183             {
184             # Argument is not a Request object; assume it's a string URL, or a
185             # URI object:
186 0           $oReq = new HTTP::Request(GET => $oReq);
187             } # unless
188 0           my $sURL = $oReq->uri;
189 0           REQUEST_READY:
190             $self->cookie_jar->add_cookie_header($oReq);
191 0           my $sReq = $oReq->as_string;
192 0 0         if ($sName ne '')
193             {
194 0           print STDERR " DDD the HTTP::Request for $sName is $sReq" if DEBUG_FETCH;
195             } # if
196 0           $self->response($self->user_agent->request($oReq));
197 0           my $sRes = $self->response->as_string;
198 0           DEBUG_FETCH && print STDERR " DDD the HTTP::Response for $sName is ==========$sRes==========";
199 0           my $sURLprev = '';
200             OBJECT_MOVED:
201 0           while ($self->response->code == 302)
202             {
203 0           print STDERR " DDD server says: Object Moved\n" if DEBUG_FETCH;
204 0           $sReq .= "\n";
205 0           $sURLprev = $sURL;
206 0           $sURL = $self->response->header('Location');
207 0           $oReq = GET $sURL;
208 0           $oReq->referer($sURLprev);
209 0           $self->cookie_jar->add_cookie_header($oReq);
210 0           print STDERR " DDD the new HTTP::Request for $sName is ", $oReq->as_string if DEBUG_FETCH;
211 0           $sReq .= $oReq->as_string;
212 0           $self->response($self->user_agent->request($oReq));
213             } # while
214             META_REFRESH:
215 0   0       while (! $iIgnoreRefresh && ($self->response->content =~ m!!i))
216             {
217 0           $sURLprev = $sURL;
218 0           $sURL = $1;
219 0           $sURL =~ s!&!&!g;
220 0           print STDERR " DDD server says: Meta-Refresh to $sURL\n" if DEBUG_FETCH;
221 0           $oReq = GET $sURL;
222 0           $oReq->referer($sURLprev);
223 0           $self->cookie_jar->add_cookie_header($oReq);
224 0           print STDERR " DDD the new HTTP::Request for $sName is ", $oReq->as_string if DEBUG_FETCH;
225 0           $sReq .= "\n";
226 0           $sReq .= $oReq->as_string;
227 0           $self->response($self->user_agent->request($oReq));
228             } # while
229 0           $sRes = $self->response->headers_as_string;
230 0 0         if (! $self->response->is_success)
231             {
232 0           my $fname1 = "Pages/$sName-fail.html";
233 0 0 0       if (($sName ne '') && (open ERR, ">$fname1"))
234             {
235 0           print STDERR " --- eBay $sName failed: can not get page: ", $self->response->status_line, "\n" if DEBUG_FETCH;
236 0           print ERR "\n\n";
237 0           print ERR "\n\n";
238 0           print ERR $self->response->content;
239 0           close ERR;
240 0           print STDERR " --- what we did get back was saved in $fname1\n" if DEBUG_FETCH;
241             } # if
242 0           return '';
243             } # unless
244 0           $sPage = $self->response->content;
245 0 0 0       if ($sPage =~ m!"SignInForm"!)
    0          
246             {
247             # We need to sign-in before we get to see the requested page:
248 0           my $fname1 = "Pages/$sName-signin.html";
249 0           if (DEBUG_FETCH && ($sName ne '') && (open PAGE, '>', $fname1))
250             {
251             print PAGE "\n\n";
252             print PAGE "\n\n";
253             print PAGE $sPage;
254             close PAGE;
255             print STDERR " DDD eBay GET $sName saved in $fname1\n" if DEBUG_FETCH;
256             } # if
257 0           print STDERR " DDD parsing ebay sign-in page...\n" if DEBUG_FETCH;
258             # Parse the
elements:
259 0           my @aoForm = HTML::Form->parse($sPage, $self->response->base);
260             # As of August 2014, the sign-in form is the first one on the page:
261 0           my $oForm = $aoForm[0];
262 0 0         unless (ref $oForm)
263             {
264 0           print STDERR " EEE eBay sign-in page contained no element!\n" if DEBUG_FETCH;
265 0           return undef;
266             } # unless
267 0           print STDERR " DDD got a FORM...\n" if DEBUG_FETCH;
268             # Insert the user's values:
269 0           $oForm->value('userid', $self->{_user});
270 0           $oForm->value('pass', $self->{_pass});
271             # Request a cookie to reduce bandwidth:
272 0           $oForm->value('keepMeSignInOption', 1);
273             # Submit the form and get our cookie:
274 0           $oReq = $oForm->click;
275 0           $oReq->referer($sURLprev);
276 0           $sURLprev = $sURL;
277 0           print STDERR " DDD CLICK is ", Dumper($oReq) if DEBUG_FETCH;
278 0           print STDERR " DDD submitting password to ebay...\n" if DEBUG_FETCH;
279 0           goto REQUEST_READY;
280             } # if we got a sign-in page
281             elsif (($sPage =~ m!"AdultLogin"!)
282             ||
283             ($sPage =~ m!Terms of Use: Mature Audiences Category!)
284             )
285             {
286 0           my $fname1 = "Pages/$sName-adultlogin.html";
287 0           if (DEBUG_FETCH && ($sName ne '') && (open PAGE, '>', $fname1))
288             {
289             print PAGE "\n\n";
290             print PAGE "\n\n";
291             print PAGE $sPage;
292             close PAGE;
293             print STDERR " DDD eBay GET $sName saved in $fname1\n" if DEBUG_FETCH;
294             } # if
295             # We need to accept the "Mature" disclaimer before we get to see
296             # the requested page. Parse the elements:
297 0           my @aoForm = HTML::Form->parse($sPage, $self->response->base);
298             # The adult-consent form is the last one on the page:
299 0           my $oForm = $aoForm[-1];
300 0 0         unless (ref $oForm)
301             {
302 0           print STDERR " --- eBay adult-consent page's was not valid?\n" if DEBUG_FETCH;
303 0           return undef;
304             } # unless
305 0           print STDERR " DDD got a FORM...\n" if DEBUG_FETCH;
306             # Submit the form and get our cookie:
307 0           $oReq = $oForm->click;
308 0           $oReq->referer($sURLprev);
309 0           $sURLprev = $sURL;
310 0           print STDERR " DDD giving adult-consent to ebay...\n" if DEBUG_FETCH;
311 0           print STDERR " DDD CLICK is ", Dumper($oReq) if DEBUG_FETCH;
312 0           goto REQUEST_READY;
313             }
314             else
315             {
316             # No special action required, we got the requested page:
317 0           my $sRes = $self->response->headers_as_string;
318 0           if (DEBUG_FETCH && ($sName ne '') && (open PAGE, '>', $fname))
319             {
320             print PAGE "\n\n";
321             print PAGE "\n\n";
322             print PAGE $sPage;
323             close PAGE;
324             print STDERR " DDD eBay GET $sName saved in $fname\n" if DEBUG_FETCH;
325             } # if
326             } # else
327 0           return $sPage;
328             } # fetch_any_ebay_page
329              
330              
331             =item any_error
332              
333             Returns non-zero if there are any error messages in the object.
334              
335             =cut
336              
337             sub any_error
338             {
339 0     0 1   shift->error ne ''
340             } # any_error
341              
342             sub _add_error
343             {
344 0     0     local $" = "";
345 0           shift->{'_error'} .= "@_";
346             } # _add_error
347              
348             =item error
349              
350             Returns a string, the most recent error message(s).
351              
352             =cut
353              
354             sub error
355             {
356 0 0   0 1   shift->{'_error'} || '';
357             } # error
358              
359             =item clear_errors
360              
361             Removes all error messages from the object.
362              
363             =cut
364              
365             sub clear_errors
366             {
367 0     0 1   shift->{'_error'} = '';
368             } # clear_errors
369              
370              
371             sub _epoch_of_date
372             {
373 0     0     return UnixDate(&ParseDate(shift), '%s');
374             } # _epoch_of_date
375              
376             =item selling_page
377              
378             Returns the HTML of the "My Selling" page for this user.
379              
380             =cut
381              
382             sub selling_page
383             {
384 0     0 1   my $self = shift;
385 0 0         if ($self->{_selling_page} ne '')
386             {
387 0           DEBUG_SELLING && print STDERR " DDD short-circuited _selling_page\n";
388 0           return $self->{_selling_page};
389             } # if
390 0           my $sUserID = $self->{_user};
391 0           my $sPasswordEncrypted = $self->signin();
392 0           print STDERR " DDD sPasswordEncrypted is ===$sPasswordEncrypted===\n" if DEBUG_FETCH;
393             # my $sURL = qq{http://cgi6.ebay.com/aw-cgi/eBayISAPI.dll?MfcISAPICommand=MyeBayItemsSelling&userid=$sUserID&pass=$sPasswordEncrypted&dayssince=30};
394 0           my $sURL = qq{http://cgi6.ebay.com/aw-cgi/ebayISAPI.dll?MyeBayItemsSelling&userid=$sUserID&pass=$sPasswordEncrypted&first=N&sellerSort=3&bidderSort=3&watchSort=3&dayssince=30};
395 0           $sURL = qq{http://my.ebay.com/ws/ebayISAPI.dll?MyeBay&userid=$sUserID&pass=$sPasswordEncrypted&first=N&sellerSort=3&bidderSort=3&watchSort=3&dayssince=30};
396 0           my $sPage = $self->fetch_any_ebay_page($sURL, 'selling');
397 0           $self->{_selling_page} = $sPage;
398 0           return $sPage;
399             } # selling_page
400              
401              
402             =item watchlist_auctions
403              
404             Returns a list of WWW::Ebay::Listing objects.
405              
406             Note that any time/dates returned will be U.S. Pacific time zone.
407              
408             =cut
409              
410             sub watchlist_auctions
411             {
412 0     0 1   my $self = shift;
413 0 0         return @{$self->{raoWatching}} if $self->{raoWatching};
  0            
414 0   0       my $sFname = shift() || '';
415 0           my $sPage = $self->selling_page;
416 0 0 0       if (($sFname ne '') && (open PAGE, '>', $sFname))
417             {
418 0           print PAGE $sPage;
419 0 0         close PAGE or warn;
420             } # if
421 0           _debug " DDD start parsing webpage...\n" if DEBUG_WATCH;
422             # Date_Init('TZ=US/Pacific');
423             # Our return value, a list of WWW::Search::Result objects:
424 0           my @aoWSR;
425              
426 0   0       my $oTree = $self->{_selling_tree} || HTML::TreeBuilder->new_from_content($sPage);
427 0 0         unless (ref $oTree)
428             {
429 0           _debug " --- can not parse the response from ebay\n";
430 0           return ();
431             } # unless
432 0           $self->{_selling_tree} = $oTree;
433 0           my @aoTDtitle = $oTree->look_down(_tag => 'td',
434             class => 'c_Title',
435             colspan => 5,
436             );
437             TITLE_TD_TAG:
438 0           foreach my $oTDtitle (@aoTDtitle)
439             {
440 0 0         next TITLE_TD_TAG unless ref $oTDtitle;
441 0           _debug " DDD got a TDtitle...\n" if DEBUG_WATCH;
442 0           my $oA = $oTDtitle->look_down(_tag => 'a');
443 0 0         next TITLE_TD_TAG unless ref $oA;
444 0           _debug " DDD has an A...\n" if DEBUG_WATCH;
445 0           my $sURL = $oA->attr('href');
446 0   0       my $sTitle = $oA->as_text || next TITLE_TD_TAG;
447 0           _debug " DDD has a title...\n" if DEBUG_WATCH;
448             # Get the parent row:
449 0           my $oTRparent = $oTDtitle->look_up(_tag => 'tr');
450 0 0         next TITLE_TD_TAG unless ref $oTRparent;
451 0           _debug " DDD has a parent TR...\n" if DEBUG_WATCH;
452             # Get the next row:
453 0           my $oTRaunt = $oTRparent->right;
454 0 0         next TITLE_TD_TAG unless ref $oTRaunt;
455 0           _debug " DDD has an aunt TR...\n" if DEBUG_WATCH;
456             # Create a new result item:
457 0           my $oWSR = new WWW::Search::Result;
458 0           $oWSR->add_url($sURL);
459 0           $oWSR->title($sTitle);
460 0           push @aoWSR, $oWSR;
461             # Get the cells of that row:
462 0           my @aoTD = $oTRaunt->look_down(_tag => 'td');
463             COUSIN_TD_TAG:
464 0           foreach my $oTD (@aoTD)
465             {
466 0 0         next COUSIN_TD_TAG unless ref $oTD;
467 0           my $sClass = $oTD->attr('class');
468 0           _debug " DDD has a $sClass TD...\n" if DEBUG_WATCH;
469 0 0         if ($sClass =~ m!price!i)
470             {
471 0           $oWSR->bid_amount($oTD->as_text);
472 0           _debug " DDD has a price TD...\n" if DEBUG_WATCH;
473             } # if CurrentPrice
474 0 0         if ($sClass =~ m!shipping!i)
    0          
    0          
    0          
    0          
    0          
    0          
475             {
476 0           $oWSR->shipping($oTD->as_text);
477 0           _debug " DDD has a shipping TD...\n" if DEBUG_WATCH;
478             } # if CurrentPrice
479             elsif ($sClass =~ m!bids!i)
480             {
481 0           my $s = $oTD->as_text;
482 0 0         $s = 0 if ($s eq '--');
483 0           $oWSR->bid_count(0 + $s);
484 0           _debug " DDD has a bids TD...\n" if DEBUG_WATCH;
485             } # if Bids
486             elsif ($sClass =~ m!bidder!i)
487             {
488 0           $oWSR->bidder($oTD->as_text);
489 0           _debug " DDD has a bidder TD...\n" if DEBUG_WATCH;
490             } # if Bids
491             elsif ($sClass =~ m!seller!i)
492             {
493 0           $oWSR->seller($oTD->as_text);
494 0           _debug " DDD has a seller TD...\n" if DEBUG_WATCH;
495             } # if Bids
496             elsif ($sClass =~ m!watchers!i)
497             {
498 0           $oWSR->watcher_count(0 + $oTD->as_text);
499 0           _debug " DDD has a watchers TD...\n" if DEBUG_WATCH;
500             } # if Watchers
501             elsif ($sClass =~ m!questions!i)
502             {
503 0           $oWSR->question_count(0 + $oTD->as_text);
504 0           _debug " DDD has a questions TD...\n" if DEBUG_WATCH;
505             } # if Questions
506             elsif ($sClass =~ m!timeleft!i)
507             {
508 0 0         my $oWSE = new WWW::Search('Ebay') or next COUSIN_TD_TAG;
509 0           $oWSE->_parse_enddate($oTD->as_text, $oWSR);
510 0           _debug " DDD has an enddate TD...\n" if DEBUG_WATCH;
511             }
512             } # foreach COUSIN_TD_TAG
513             } # foreach TITLE_TD_TAG
514 0           $self->{raoWatching} = \@aoWSR;
515 0           return @aoWSR;
516             } # watchlist_auctions
517              
518              
519             =item selling_auctions
520              
521             Returns a list of WWW::Ebay::Listing objects representing the auctions
522             currently active.
523              
524             Note that any time/dates returned will be U.S. Pacific time zone.
525              
526             =cut
527              
528             sub selling_auctions
529             {
530 0     0 1   my $self = shift;
531 0 0         return @{$self->{raoSelling}} if $self->{raoSelling};
  0            
532 0   0       my $sFname = shift() || '';
533 0           my $sPage = $self->selling_page;
534 0 0 0       if (($sFname ne '') && (open PAGE, '>', $sFname))
535             {
536 0           print PAGE $sPage;
537 0 0         close PAGE or warn;
538             } # if
539 0           _debug " DDD start parsing webpage...\n" if DEBUG_SELLING;
540             # Date_Init('TZ=US/Pacific');
541             # Our return value, a list of WWW::Ebay::Listing objects:
542 0           my @aoWEL;
543              
544 0   0       my $oTree = $self->{_selling_tree} || HTML::TreeBuilder->new_from_content($sPage);
545 0 0         unless (ref $oTree)
546             {
547 0           _debug " --- can not parse the response from ebay\n";
548 0           return ();
549             } # unless
550 0           $self->{_selling_tree} = $oTree;
551             PARSE_SELLING_SECTION:
552 0           while (1)
553             {
554             # This is a fake (infinite) loop which allows us to use 'last'
555             # rather than 'goto'.
556 0           my $iCount = 0;
557             my $oAselling = $oTree->look_down('_tag' => 'span',
558             class => 'B',
559 0     0     sub { $_[0]->as_text eq q(Items I'm Selling) },
560 0           );
561 0 0         if (ref $oAselling)
562             {
563 0           DEBUG_SELLING && _debug(" DDD found for SELLING section: ", $oAselling->as_HTML, "\n");
564 0           $oAselling = $oAselling->look_up(_tag => 'td');
565 0 0         last PARSE_SELLING_SECTION if ! ref($oAselling);
566 0           DEBUG_SELLING && _debug(" DDD parent is ==", $oAselling->as_HTML, "==\n");
567 0           my $s = $oAselling->as_text;
568 0           $s =~ m!\s+\(\s*(\d+)\s+ITEM!i;
569 0   0       $iCount = $1 || 0;
570 0           print STDERR " DDD there should be $iCount SELLING auctions\n" if DEBUG_SELLING;
571             } # if
572             else
573             {
574 0           $self->_add_error("Did not find for SELLING section. ");
575             }
576 0 0         if ($iCount <= 0)
577             {
578 0           last PARSE_SELLING_SECTION;
579             } # if
580 0           my $oTable = $oTree->look_down(_tag => 'table',
581             id => 'Selling',
582             );
583 0 0         if (! ref $oTable)
584             {
585 0           $self->_add_error("Did not find for SELLING section. ");
586 0           last PARSE_SELLING_SECTION;
587             } # if
588 0           my @asColumns = qw( spacer price bids bidder watchers questions time_left );
589 0           DEBUG_SELLING && _debug(" DDD selling is ==", $oTable->as_HTML, "==\n"); containing selling auction title ==", $oTR->as_HTML, "==\n"); for ITEM. "); containing selling auction details ==", $oTR->as_HTML, "==\n");
590 0           my @aoTR = $oTable->look_down('_tag' => 'tr');
591             # Throw out the header row:
592 0           shift @aoTR;
593             TR:
594 0           while (my $oTR = shift @aoTR)
595             {
596 0           my ($oTD, $s);
597 0 0         next unless ref $oTR;
598             # Got a row containing an auction. Actually they are pairs of
599             # rows; one row has the auction title, the next row has all the
600             # details.
601 0           DEBUG_SELLING && _debug(" DDD
602             my $oA = $oTR->look_down('_tag' => 'a',
603             sub
604             {
605 0 0   0     defined($_[0]->attr('href'))
606             &&
607             $_[0]->attr('href') =~ m!ViewItem!
608             },
609 0           );
610 0 0         next TR unless ref $oA;
611             # Make sure this is really an auction title/link:
612 0 0         next TR unless defined($oA->attr('href'));
613 0           my $sURL = $oA->attr('href');
614 0 0         next TR unless ($sURL =~ m!ViewItem!);
615 0 0         next TR unless ($sURL =~ m!item=(\d+)!);
616 0           my $iItem = $1;
617             # OK, we've got an auction.
618 0           my $oWEL = new WWW::Ebay::Listing;
619 0           my $sTitle = $oA->as_text;
620 0           $sTitle =~ s![\s\t\r\n]+\Z!!;
621 0           $oWEL->title($sTitle);
622 0           $oWEL->id($iItem);
623 0           $oWEL->status->listed('yes');
624 0           print STDERR " DDD title ==$sTitle==\n" if DEBUG_SELLING;
625             # Go to the next row, where we should find the auction details:
626 0           $oTR = $oTR->right; # shift @aoTR;
627 0 0         if (! ref($oTR))
628             {
629 0           $self->_add_error("Did not find slave
630 0           next TR;
631             } # if
632 0           DEBUG_SELLING && _debug(" DDD
633 0           my @aoTD = $oTR->look_down('_tag' => 'td');
634             SELLING_COLUMN:
635 0           foreach my $sCol (@asColumns)
636             {
637 0           $oTD = shift @aoTD;
638 0 0         if (! ref($oTD))
639             {
640 0           $self->_add_error("Did not find for $sCol column. ");
641 0           next TR;
642             } # if
643 0 0         if ($sCol eq 'price')
    0          
    0          
    0          
    0          
    0          
644             {
645 0           $s = $oTD->as_text;
646             # Keep just the numeric portion:
647 0           $s =~ tr!.0123456789!!dc;
648 0 0         if ($s !~ m!\d!)
649             {
650 0           $self->_add_error("ITEM's current bid '$s' is not a number. ");
651 0           next TR;
652             } # if
653             # Convert dollars to cents:
654 0           $oWEL->bidmax(int(eval($s) * 100));
655             }
656             elsif ($sCol eq 'bids')
657             {
658             # Column 3 = Number of Bids
659 0           $s = $oTD->as_text;
660 0 0         $s = 0 if $s =~ m!n/a!;
661 0           $oWEL->bidcount($s);
662             }
663             elsif ($sCol eq 'bidder')
664             {
665             # Column 4 = current bidder
666             }
667             elsif ($sCol eq 'watchers')
668             {
669             # Column 5 = number of watchers
670             }
671             elsif ($sCol eq 'questions')
672             {
673             # Column 6 = number of questions
674             }
675             elsif ($sCol eq 'time_left')
676             {
677             # Column 7 = Time Left
678 0           my $sDateRaw = my $sDate = $oTD->as_text;
679 0           $sDate =~ s!d! days!;
680 0           $sDate =~ s!h! hours!;
681 0           $sDate =~ s!m! minutes!;
682 0           my $date = DateCalc('now', " + $sDate");
683 0           my $sDateEnd = _epoch_of_date($date);
684 0           $oWEL->dateend($sDateEnd);
685 0           print STDERR " DDD end date: raw ==$sDateRaw== cooked ==$sDate== date==$date==\n" if DEBUG_SELLING;
686             }
687             } # foreach SELLING_COLUMN
688 0           push @aoWEL, $oWEL;
689             } # while $oTR
690 0           last PARSE_SELLING_SECTION;
691             } # end of fake while(1) loop for PARSE_SELLING_SECTION
692 0           $self->{raoSelling} = \@aoWEL;
693 0           return @aoWEL;
694             } # selling_auctions
695              
696              
697             =item sold_auctions
698              
699             Returns a list of WWW::Ebay::Listing objects representing the auctions
700             that have ended and received bids.
701              
702             Note that any time/dates returned will be U.S. Pacific time zone.
703              
704             =cut
705              
706             sub sold_auctions
707             {
708 0     0 1   my $self = shift;
709 0 0         return @{$self->{raoSold}} if $self->{raoSold};
  0            
710 0   0       my $sFname = shift() || '';
711 0           my $sPage = $self->selling_page;
712 0 0 0       if (($sFname ne '') && (open PAGE, '>', $sFname))
713             {
714 0           print PAGE $sPage;
715 0 0         close PAGE or warn;
716             } # if
717 0           _debug " DDD start parsing webpage...\n" if DEBUG_SOLD;
718             # Date_Init('TZ=US/Pacific');
719             # Our return value, a list of WWW::Ebay::Listing objects:
720 0           my $oTree;
721 0 0         if (ref $self->{_selling_tree})
722             {
723 0           $oTree = $self->{_selling_tree};
724 0           DEBUG_SOLD && print STDERR " DDD short-circuited _selling_tree\n";
725             }
726             else
727             {
728 0           $oTree = HTML::TreeBuilder->new_from_content($sPage);
729 0 0         unless (ref $oTree)
730             {
731 0           _debug " --- can not parse the response from ebay\n";
732 0           return ();
733             } # unless
734 0           $self->{_selling_tree} = $oTree;
735             }
736 0           my @aoWEL;
737             PARSE_SOLD_SECTION:
738 0           while (1)
739             {
740 0           my $iCount = 0;
741             my $oA = $oTree->look_down('_tag' => 'span',
742             class => 'B',
743 0     0     sub { $_[0]->as_text eq q(Items I've Sold) },
744 0           );
745 0 0         if (ref $oA)
746             {
747 0           DEBUG_SOLD && _debug(" DDD found for SOLD section: ", $oA->as_HTML, "\n");
748 0           $oA = $oA->parent;
749 0           my $s = $oA->as_text;
750 0           $iCount = -1;
751 0 0         if ($s =~ m!\(\s*(\d+)\s+ITEM!i)
752             {
753 0           $iCount = $1;
754 0           DEBUG_SOLD && _debug(" DDD there should be $iCount sold auctions\n");
755             } # if
756             } # if
757             else
758             {
759 0           $self->_add_error("Did not find for SOLD section. ");
760 0           last PARSE_SOLD_SECTION;
761             }
762 0 0         last PARSE_SOLD_SECTION if ($iCount < 0);
763 0           my $oTable = $oTree->look_down(_tag => 'table',
764             id => 'Sold',
765             );
766 0 0         if (! ref $oTable)
767             {
768 0           $self->_add_error("Did not find for SOLD section. ");
769 0           last PARSE_SOLD_SECTION;
770             } # if
771             # print STDERR " DDD sold is ==", $oTable->as_HTML, "==\n" if DEBUG_SOLD; containing seller ==", $oTR->as_HTML, "==\n") if (2 < DEBUG_SOLD); of next row ==", $oTR->as_HTML, "==\n");
772 0           my @aoTR = $oTable->look_down(_tag => 'tr',
773             bgcolor => '#f4f4f4',
774             );
775             SOLD_TR:
776 0           while (my $oTR = shift @aoTR)
777             {
778 0           my ($oTD, $s);
779 0 0         next SOLD_TR unless ref $oTR;
780             # Got a row containing an auction. Actually they are groups of
781             # rows; one row has the buyer's ID, the next rows have all the
782             # auctions that person won.
783 0           _debug(" DDD
784 0           my @aoTD = $oTR->look_down(_tag => 'td');
785             # Column 1 = checkbox:
786 0           $oTD = shift @aoTD;
787             # Column 2 = winner:
788 0           $oTD = shift @aoTD;
789 0           my $oA = $oTD->look_down('_tag' => 'strong');
790 0 0         next SOLD_TR unless ref $oA;
791 0           my $sWinnerID = $oA->as_text;
792             # In case this person won one auction, all the details are in
793             # this row:
794              
795 0           my $oWEL = new WWW::Ebay::Listing;
796 0           $oWEL->winnerid($sWinnerID);
797             # We know this auction has ended because this is the "sold"
798             # section of the page:
799 0           $oWEL->status->listed('yes');
800 0           $oWEL->status->ended('yes');
801             # Next column = quantity:
802 0           $oTD = shift @aoTD;
803 0           DEBUG_SOLD && _debug(" DDD quantity ==", $oTD->as_HTML, "==\n");
804             # next Column = Bid Price
805 0           $oTD = shift @aoTD;
806 0 0         if (! ref($oTD))
807             {
808 0           $self->_add_error("Did not find for SOLD ITEM end price. ");
809 0           next SOLD_TR;
810             } # if
811 0           DEBUG_SOLD && _debug(" DDD containing EndPrice ==", $oTD->as_HTML, "==\n");
812 0           $s = $oTD->as_text;
813 0           print STDERR " DDD raw End Price is ==$s==\n" if DEBUG_SOLD;
814 0           $s =~ tr!.0123456789!!dc;
815             # Convert dollars to cents:
816 0           my $iBidCents = int((0.005 + $s) * 100);
817 0           print STDERR " DDD Bid Cents is ==$iBidCents==\n" if DEBUG_SOLD;
818 0           $oWEL->bidmax($iBidCents);
819             # next Column = Total Price with shipping. If the buyer has not
820             # done checkout (and the seller has not sent an invoice), this
821             # will be '--'.
822 0           $oTD = shift @aoTD;
823 0           DEBUG_SOLD && _debug(" DDD of total price ==", $oTD->as_HTML, "==\n");
824 0   0       $s = $oTD->as_text || '';
825 0           print STDERR " DDD raw Total Price is ==$s==\n" if DEBUG_SOLD;
826 0 0         if ($s eq '--')
827             {
828 0           $oWEL->shipping('unknown');
829             }
830             else
831             {
832 0           $s =~ tr!.0123456789!!dc;
833 0 0         if ($s !~ m!\d!)
834             {
835 0           $self->_add_error("sold item's total price is not a number. ");
836 0           next SOLD_TR;
837             } # if
838             # Convert dollars to cents:
839 0           my $iTotalCents = int((0.005 + $s) * 100);
840 0           print STDERR " DDD Total Cents is ==$iTotalCents==\n" if DEBUG_SOLD;
841 0           my $iShippingCents = $iTotalCents - $iBidCents;
842 0           $oWEL->shipping($iShippingCents);
843             } # else
844             # Go to the next row:
845 0           $oTR = $oTR->left;
846 0 0         if (! ref $oTR)
847             {
848 0           next SOLD_TR;
849             } # if
850 0           DEBUG_SOLD && _debug(" DDD
851 0           $oA = $oTR->look_down(_tag => 'a');
852 0 0         next SOLD_TR unless ref $oA;
853 0           DEBUG_SOLD && _debug(" DDD of title ==", $oA->as_HTML, "==\n");
854 0           my $sTitle = $oA->as_text;
855 0           $sTitle =~ s![\s\t\r\n]+\Z!!;
856 0           $oWEL->title($sTitle);
857 0           my $sURL = $oA->attr('href');
858 0 0         next SOLD_TR unless ($sURL =~ m!ViewItem!);
859 0 0         next SOLD_TR unless ($sURL =~ m!item=(\d+)!);
860 0           my $iItem = $1;
861 0           $oWEL->id($iItem);
862 0           push @aoWEL, $oWEL;
863             } # while
864 0           last PARSE_SOLD_SECTION;
865             } # end of fake while(1) loop for PARSE_SOLD_SECTION
866 0           $self->{raoSold} = \@aoWEL;
867 0           return @aoWEL;
868             } # sold_auctions
869              
870              
871             =item unsold_auctions
872              
873             Returns a list of WWW::Ebay::Listing objects representing the auctions
874             that have ended but received no bids.
875              
876             Note that any time/dates returned will be U.S. Pacific time zone.
877              
878             =cut
879              
880             sub unsold_auctions
881             {
882 0     0 1   my $self = shift;
883 0 0         return @{$self->{raoUnsold}} if $self->{raoUnsold};
  0            
884 0   0       my $sFname = shift() || '';
885 0           my $sPage = $self->selling_page;
886 0 0 0       if (($sFname ne '') && (open PAGE, '>', $sFname))
887             {
888 0           print PAGE $sPage;
889 0 0         close PAGE or warn;
890             } # if
891 0           _debug " DDD start parsing webpage...\n" if DEBUG_UNSOLD;
892             # Date_Init('TZ=US/Pacific');
893             # Our return value, a list of WWW::Ebay::Listing objects:
894 0           my @aoWEL;
895              
896 0   0       my $oTree = $self->{_selling_tree} || HTML::TreeBuilder->new_from_content($sPage);
897 0 0         unless (ref $oTree)
898             {
899 0           _debug " --- can not parse the response from ebay\n";
900 0           return ();
901             } # unless
902 0           $self->{_selling_tree} = $oTree;
903             PARSE_UNSOLD_SECTION:
904 0           while (1)
905             {
906             # This is a fake (infinite) loop which allows us to use 'last'
907             # rather than 'goto'.
908 0           my $iCount = 0;
909 0           my $oAunsold = $oTree->look_down('_tag' => 'a',
910             'name' => 'unsold',
911             );
912 0 0         if (ref $oAunsold)
913             {
914 0           print STDERR " DDD found for UNSOLD section: ", $oAunsold->as_HTML, "\n" if DEBUG_UNSOLD;
915 0           my $s = $oAunsold->as_text;
916 0           $s =~ m!\(\s*(\d+)\s+Items?!;
917 0   0       $iCount = $1 || 0;
918 0           print STDERR " DDD there should be $iCount UNSOLD auctions\n" if DEBUG_UNSOLD;
919             } # if
920 0 0         if ($iCount <= 0)
921             {
922 0           last PARSE_UNSOLD_SECTION;
923             } # if
924 0           my $oTable = $oAunsold->look_up('_tag' => 'table');
925 0 0         if (! ref $oTable)
926             {
927 0           $self->_add_error("Did not find master for UNSOLD section. ");
928 0           last PARSE_UNSOLD_SECTION;
929             } # if
930 0           print STDERR " DDD ancestor is ==", $oTable->as_HTML, "==\n" if DEBUG_UNSOLD;
931             # The heart of the matter is in the n-th table over from this one:
932 0           my $iTable = 2;
933             do
934 0           {
935 0           $oTable = $oTable->right;
936 0 0         if (ref $oTable)
937             {
938 0 0         $iTable-- if ($oTable->tag eq 'table');
939             } # if
940             else
941             {
942             # bail!
943 0           $oTable = 0;
944             }
945             } until ($iTable < 1);
946 0 0         if (! ref $oTable)
947             {
948 0           $self->_add_error("Did not find slave for UNSOLD section. ");
949 0           last PARSE_UNSOLD_SECTION;
950             } # if
951 0           print STDERR " DDD n-th TABLE sibling of ancestor is ==", $oTable->as_HTML, "==\n" if DEBUG_UNSOLD; containing unsold auction title ==", $oTR->as_HTML, "==\n" if DEBUG_UNSOLD;
952 0           my @aoTR = $oTable->look_down('_tag' => 'tr');
953             TR:
954 0           while (my $oTR = shift @aoTR)
955             {
956 0           my ($oTD, $s);
957 0 0         next unless ref $oTR;
958             # Got a row containing an auction. Actually they are pairs of
959             # rows; one row has the auction title, the next row has all the
960             # details.
961 0           print STDERR " DDD
962 0           my $oA = $oTR->look_down('_tag' => 'a');
963 0 0         next TR unless ref $oA;
964             # Make sure this is really an auction title/link:
965 0 0         next TR unless defined($oA->attr('href'));
966 0 0         next TR unless ($oA->attr('href') =~ m!ViewItem!);
967             # OK, we've got an auction.
968 0           my $oWEL = new WWW::Ebay::Listing;
969 0           my $sTitle = $oA->as_text;
970 0           $sTitle =~ s![\s\t\r\n]+\Z!!;
971 0           $oWEL->title($sTitle);
972 0           print STDERR " DDD title ==$sTitle==\n" if DEBUG_UNSOLD;
973 0           $oTD = $oA->look_up('_tag' => 'td');
974 0 0         next TR unless ref $oTD;
975 0           $oTD = $oTD->left;
976 0 0         next TR unless ref $oTD;
977 0           print STDERR " DDD containing Item# ==", $oTD->as_HTML, "==\n" if DEBUG_UNSOLD;
978 0           $s = $oTD->as_text;
979             # Delete all but numbers:
980 0           $s =~ tr!0123456789!!dc;
981 0           $oWEL->id($s);
982 0           $oWEL->status->listed('yes');
983 0           $oWEL->status->ended('yes');
984 0           push @aoWEL, $oWEL;
985             } # while $oTR
986 0           last PARSE_UNSOLD_SECTION;
987             } # end of fake while(1) loop for PARSE_UNSOLD_SECTION
988 0           $self->{raoUnsold} = \@aoWEL;
989 0           return @aoWEL;
990             } # unsold_auctions
991              
992             # =item get_user_email
993              
994             # Takes two arguments: the eBay userid of the person whose email you seek;
995             # and an auction ID in which you and that person were involved together.
996              
997             # Returns that user's email address.
998             # If an error occurs, prints an error message to STDOUT and returns empty string.
999              
1000             # =cut
1001              
1002             # eBay does not allow users to obtain other user's email. We have to
1003             # use ebay's interface to send an email message to another user.
1004              
1005             sub _get_user_email_OLD
1006             {
1007 0     0     my $self = shift;
1008 0           my ($sUserID, $iAuctionID) = @_;
1009 0           DEBUG_EMAIL && _debug(" DDD get_user_email($sUserID,$iAuctionID)\n");
1010              
1011             #
1012 0           my $sURL = 'http://contact.ebay.com/ws1/eBayISAPI.dll?MfcISAPICommand=ReturnUserEmail&requested=__USER__&frm=284&iid=__AUCTION__&de=off&redirect=0';
1013 0           $sURL =~ s!__USER__!$sUserID!e;
  0            
1014 0           $sURL =~ s!__AUCTION__!$iAuctionID!e;
  0            
1015 0           DEBUG_EMAIL && _debug(" DDD url ==$sURL==\n");
1016 0           my $sPage = $self->fetch_any_ebay_page($sURL, 'contact');
1017 0 0         if ($sPage =~ m!\shref="mailto:(.+?)"!)
1018             {
1019 0           return $1;
1020             } # if
1021 0           DEBUG_EMAIL && _debug(" --- parse error: can not parse user-email page\n");
1022 0           return '';
1023             } # _get_user_email_OLD
1024              
1025              
1026             =item cookie_jar
1027              
1028             =cut
1029              
1030             sub cookie_jar
1031             {
1032 0     0 1   my $self = shift;
1033 0   0       my $arg = shift() || 0;
1034 0           DEBUG_FUNC && _debug(" DDD Ebay::Session::c_jar($arg)\n");
1035 0 0         if ($arg)
1036             {
1037             # If argument is given, replace current jar:
1038 0           $self->{_cookie_jar} = $arg;
1039             } # if
1040             # If jar is still not defined, create one:
1041 0   0       $self->{_cookie_jar} ||= new HTTP::Cookies;
1042             # Return the jar:
1043 0           $self->{_cookie_jar};
1044             } # cookie_jar
1045              
1046              
1047             =item user_agent
1048              
1049             Returns a user_agent suitable for requesting Ebay webpages.
1050             If you need special processing on your network, you can override this method.
1051             You need to set the cookie_jar to $self->cookie_jar.
1052              
1053             =cut
1054              
1055             sub user_agent
1056             {
1057 0     0 1   my $self = shift;
1058 0           DEBUG_FUNC && _debug(" DDD Ebay::Session::user_agent()\n");
1059 0 0         if (! ref $self->{_user_agent})
1060             {
1061 0           my $ua = WWW::Search::_load_env_useragent();
1062 0 0         if (! ref $ua)
1063             {
1064             # print STDERR " XXX WWW::Search::_load_env_useragent() failed\n";
1065 0           $ua = new LWP::UserAgent;
1066 0           $ua->env_proxy('yes');
1067             } # if
1068 0           $ua->cookie_jar($self->cookie_jar);
1069             # print STDERR " III ua is $ua\n";
1070 0           $self->{_user_agent} = $ua;
1071             } # if
1072 0           $self->{_user_agent};
1073             } # user_agent
1074              
1075             =back
1076              
1077             =cut
1078              
1079             sub _send_email_form
1080             {
1081             return <
1082            
1083            
1084            
1085            
1086            
1087            
1088            
1089            
1090            
1091             Enter your message below. eBay will send an email to the member.
1092            

1093            
1094            
1095            
1096            
1097            
1098            
1099            
1100            
1101            
1102            
1103            
1104             To:
1105             watto2000
1106             Marketplace Safety Tips

eBay rules prohibit use of this Contact eBay Member feature to offer to buy or sell directly without bidding on and winning the item on eBay. We strongly advise recipients of these email offers to report them to eBay. Participants in these 'off eBay' transactions lose their ability to use eBay Feedback and our buyer protection programs. Learn more about trading safely.
1107            
1108            
1109             Subject:
1110            
1111            
1112            
1113            
Enter up to 1000 characters. HTML cannot be displayed.
1114            
1115            
1116            
1117            
1118            
1119            
1120            
1121            
1122            
1123            
1124            
1125            
1126             Send a copy of this email to myself.
1127            
1128            
1129            
1130            
1131             Hide my email address for privacy purposes.
1132            
1133            
1134            
1135            
1136            
1137            
1138            
1139            
1140            
1141            
1142            
1147            
1148            
1149            
1150            
1151            
1152            
1153            
1154             ENDEMAILFORM
1155 0     0     } # _send_email_form
1156              
1157             1;
1158              
1159             __END__