itunes-connect 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe ItunesConnect::Commands::Report do
4
+ before(:each) do
5
+ @cmd = ItunesConnect::Commands::Report.new(mock(:null_object => true))
6
+ @defaults = {
7
+ :db => '/tmp/store.db',
8
+ :summarize? => false,
9
+ :to => nil,
10
+ :from => nil,
11
+ :country => nil,
12
+ :no_header? => false,
13
+ :delimiter => "\t",
14
+ :total? => false
15
+ }
16
+ end
17
+
18
+ describe 'with valid execution arguments' do
19
+ before(:each) do
20
+ @store = mock(ItunesConnect::Store)
21
+ ItunesConnect::Store.should_receive(:new).
22
+ with("/tmp/store.db").
23
+ and_return(@store)
24
+ @io = StringIO.new
25
+ @data = [
26
+ mock(:report_date => Date.parse('2009/09/09'), :country => 'US',
27
+ :install_count => 1, :update_count => 2),
28
+ mock(:report_date => Date.parse('2009/09/09'), :country => 'GB',
29
+ :install_count => 3, :update_count => 4)
30
+ ]
31
+ end
32
+
33
+ it 'should request counts with no options with no qualifiers' do
34
+ @store.should_receive(:counts).and_return(@data)
35
+ clip = stub(@defaults.merge(:db => '/tmp/store.db'))
36
+ @cmd.execute!(clip, [], @io)
37
+ @io.string.should == <<EOF
38
+ Date\tCountry\tInstalls\tUpgrades
39
+ 2009-09-09\tUS\t1\t2
40
+ 2009-09-09\tGB\t3\t4
41
+ EOF
42
+ end
43
+
44
+ it 'should output data with other options' do
45
+ @store.should_receive(:counts).
46
+ with(:to => Date.parse('2009/09/09'),
47
+ :from => Date.parse('2009/09/01'),
48
+ :country => 'US').
49
+ and_return(@data)
50
+
51
+ clip = stub(@defaults.merge(:db => '/tmp/store.db',
52
+ :to => Date.parse('2009/09/09'),
53
+ :from => Date.parse('2009/09/01'),
54
+ :country => 'US'))
55
+
56
+ @cmd.execute!(clip, [], @io)
57
+ @io.string.should == <<EOF
58
+ Date\tCountry\tInstalls\tUpgrades
59
+ 2009-09-09\tUS\t1\t2
60
+ 2009-09-09\tGB\t3\t4
61
+ EOF
62
+ end
63
+
64
+ it 'should suppress the header when requested' do
65
+ @store.should_receive(:counts). and_return(@data)
66
+ clip = stub(@defaults.merge(:no_header? => true))
67
+ @cmd.execute!(clip, [], @io)
68
+ @io.string.should == <<EOF
69
+ 2009-09-09\tUS\t1\t2
70
+ 2009-09-09\tGB\t3\t4
71
+ EOF
72
+ end
73
+
74
+ it 'should separate fields with specified delimiter' do
75
+ @store.should_receive(:counts). and_return(@data)
76
+ clip = stub(@defaults.merge(:delimiter => "|"))
77
+ @cmd.execute!(clip, [], @io)
78
+ @io.string.should == <<EOF
79
+ Date|Country|Installs|Upgrades
80
+ 2009-09-09|US|1|2
81
+ 2009-09-09|GB|3|4
82
+ EOF
83
+ end
84
+
85
+ it 'should output totals when the "totals" flag is specified' do
86
+ @store.should_receive(:counts). and_return(@data)
87
+ clip = stub(@defaults.merge(:total? => true))
88
+ @cmd.execute!(clip, [], @io)
89
+ @io.string.should == <<EOF
90
+ Date\tCountry\tInstalls\tUpgrades
91
+ 2009-09-09\tUS\t1\t2
92
+ 2009-09-09\tGB\t3\t4
93
+ Total\t-\t4\t6
94
+ EOF
95
+ end
96
+ end
97
+
98
+ describe 'with :group option specified' do
99
+ before(:each) do
100
+ @store = mock(ItunesConnect::Store)
101
+ ItunesConnect::Store.should_receive(:new).
102
+ with('/tmp/store.db').
103
+ and_return(@store)
104
+
105
+ @io = StringIO.new
106
+ @data = [
107
+ mock(:country => 'US', :install_count => 1, :update_count => 2),
108
+ mock(:country => 'GB', :install_count => 3, :update_count => 4)
109
+ ]
110
+ end
111
+
112
+ it 'should request grouped country data' do
113
+ @store.should_receive(:country_counts).and_return(@data)
114
+ clip = stub(@defaults.merge(:summarize? => true))
115
+ @cmd.execute!(clip, [], @io)
116
+ @io.string.should == <<EOF
117
+ Country\tInstalls\tUpgrades
118
+ US\t1\t2
119
+ GB\t3\t4
120
+ EOF
121
+ end
122
+
123
+ it 'should suppress the header when requested' do
124
+ @store.should_receive(:country_counts).and_return(@data)
125
+ clip = stub(@defaults.merge(:summarize? => true, :no_header? => true))
126
+ @cmd.execute!(clip, [], @io)
127
+ @io.string.should == <<EOF
128
+ US\t1\t2
129
+ GB\t3\t4
130
+ EOF
131
+ end
132
+
133
+ it 'should separate fields with the specified delimiter' do
134
+ @store.should_receive(:country_counts).and_return(@data)
135
+ clip = stub(@defaults.merge(:summarize? => true, :delimiter => '|'))
136
+ @cmd.execute!(clip, [], @io)
137
+ @io.string.should == <<EOF
138
+ Country|Installs|Upgrades
139
+ US|1|2
140
+ GB|3|4
141
+ EOF
142
+ end
143
+
144
+ it 'should output totals when the "totals" flag is specified' do
145
+ @store.should_receive(:country_counts).and_return(@data)
146
+ clip = stub(@defaults.merge(:summarize? => true, :total? => true))
147
+ @cmd.execute!(clip, [], @io)
148
+ @io.string.should == <<EOF
149
+ Country\tInstalls\tUpgrades
150
+ US\t1\t2
151
+ GB\t3\t4
152
+ Total\t4\t6
153
+ EOF
154
+ end
155
+ end
156
+
157
+ describe 'with invalid execution arguments' do
158
+ it 'should require the :db option' do
159
+ lambda { @cmd.execute! }.should raise_error(ArgumentError)
160
+ end
161
+ end
162
+
163
+ describe 'command-line option parsing' do
164
+ it 'should add appropriate options to a given Clip' do
165
+ clip = mock("Clip")
166
+ clip.should_receive(:opt).
167
+ with('b', 'db',
168
+ :desc => 'Dump report to sqlite DB at the given path')
169
+ clip.should_receive(:opt).
170
+ with('c', 'country',
171
+ :desc => 'A two-letter country code to filter results with')
172
+ clip.should_receive(:opt).
173
+ with('f', 'from',
174
+ :desc => 'The starting date, inclusive')
175
+ clip.should_receive(:opt).
176
+ with('t', 'to',
177
+ :desc => 'The ending date, inclusive')
178
+ clip.should_receive(:flag).
179
+ with('s', 'summarize',
180
+ :desc => 'Summarize results by country code')
181
+ clip.should_receive(:flag).
182
+ with('n', 'no-header',
183
+ :desc => 'Suppress the column headers on output')
184
+ clip.should_receive(:opt).
185
+ with('d', 'delimiter',
186
+ :desc => 'The delimiter to use for output (normally TAB)',
187
+ :default => "\t")
188
+ clip.should_receive(:flag).
189
+ with('o', 'total', :desc => 'Add totals at the end of the report')
190
+
191
+ ItunesConnect::Commands::Report.new(clip)
192
+ end
193
+ end
194
+
195
+ end
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe ItunesConnect::Commands do
4
+
5
+ describe 'all' do
6
+ it 'should return all available command' do
7
+ ItunesConnect::Commands.all.should == [
8
+ ItunesConnect::Commands::Download,
9
+ ItunesConnect::Commands::Import,
10
+ ItunesConnect::Commands::Report,
11
+ ItunesConnect::Commands::Help
12
+ ]
13
+ end
14
+ end
15
+
16
+ describe 'for_name' do
17
+ before(:each) do
18
+ @clip = mock(:null_object => true)
19
+ end
20
+
21
+ it 'should return Download for download' do
22
+ ItunesConnect::Commands.for_name('download', @clip).
23
+ should be_kind_of(ItunesConnect::Commands::Download)
24
+ end
25
+
26
+ it 'should return Import for import' do
27
+ ItunesConnect::Commands.for_name('import', @clip).
28
+ should be_kind_of(ItunesConnect::Commands::Import)
29
+ end
30
+
31
+ it 'should return Report for report' do
32
+ ItunesConnect::Commands.for_name('report', @clip).
33
+ should be_kind_of(ItunesConnect::Commands::Report)
34
+ end
35
+
36
+ it 'should return Help for help' do
37
+ ItunesConnect::Commands.for_name('help', @clip).
38
+ should be_kind_of(ItunesConnect::Commands::Help)
39
+ end
40
+
41
+
42
+ it 'should return nil for other names' do
43
+ ItunesConnect::Commands.for_name('foobar', @clip).should be_nil
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require "fakeweb"
3
+
4
+ describe ItunesConnect::Connection do
5
+ before(:each) do
6
+ @itc = ItunesConnect::Connection.new("foo", "bar")
7
+ FakeWeb.allow_net_connect = false
8
+ end
9
+
10
+ it 'should reject invalid periods' do
11
+ lambda {
12
+ @itc.get_report(Date.today - 1, $stdout, "Invalid")
13
+ }.should raise_error(ArgumentError)
14
+ end
15
+
16
+ it 'should reject dates newer than yesterday' do
17
+ lambda {
18
+ @itc.get_report(Date.today, $stdout)
19
+ }.should raise_error(ArgumentError)
20
+
21
+ lambda {
22
+ @itc.get_report(Date.today + 1, $stdout)
23
+ }.should raise_error(ArgumentError)
24
+ end
25
+
26
+ end
@@ -0,0 +1,365 @@
1
+ HTTP/1.1 200 Apple
2
+ Date: Tue, 25 Aug 2009 18:19:18 GMT
3
+ Server: Apache/1.3.33 (Darwin) mod_ssl/2.8.24 OpenSSL/0.9.7l
4
+ Cache-Control: max-age=60
5
+ Expires: Tue, 25 Aug 2009 18:20:18 GMT
6
+ cache-control: private
7
+ cache-control: no-cache
8
+ cache-control: no-store
9
+ cache-control: must-revalidate
10
+ cache-control: max-age=0
11
+ expires: Tue, 25-Aug-2009 16:36:29 GMT
12
+ pragma: no-cache
13
+ content-length: 11640
14
+ Keep-Alive: timeout=15, max=100
15
+ Connection: Keep-Alive
16
+ Content-Type: text/html
17
+
18
+ <html>
19
+ <head>
20
+ <title>Piano-Sign In</title>
21
+
22
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
23
+ <meta name="Author" content="Apple Computer, Inc.">
24
+
25
+ <link rel="home" href="http://www.apple.com/">
26
+ <link rel="index" href="http://www.apple.com/find/sitemap.html">
27
+
28
+
29
+
30
+ <script language="JavaScript">
31
+
32
+ function browserAlert()
33
+ {
34
+ //alert(navigator.userAgent);
35
+ //alert(document.getElementById("checkAlrt").value);
36
+
37
+ var alertMessage ="The browser you are using is not supported by this application. The application may not work as expected. Supported browsers are:- Safari 2 and above in Mac & IE 6.0 and above in Windows & Firefox 2 and above in both Mac and Windows";
38
+ if(document.getElementById("checkAlrt").value!="Message Displayed")
39
+ {
40
+
41
+ var browserType = navigator.userAgent;
42
+ var s = browserType.length;
43
+ var i = browserType.lastIndexOf("/")+1;
44
+ //alert("Split :::::" + navigator.userAgent.split("/"));
45
+ tempVersion = navigator.userAgent.split("/");
46
+ //alert(tempVersion[3]);
47
+ var iSafari = browserType.indexOf("Safari");
48
+ var iNetscape = browserType.indexOf("Netscape");
49
+ var iFirefox = browserType.indexOf("Firefox");
50
+ var iIE = browserType.indexOf("IE");
51
+ var iMac = browserType.indexOf("Mac");
52
+ var iOpera = browserType.indexOf("Opera");
53
+
54
+
55
+ if(iSafari < 0 && iFirefox < 0 && iIE < 0 )
56
+ {
57
+ alert(alertMessage);
58
+ }
59
+
60
+ else
61
+ {
62
+
63
+ if(iSafari > -1)
64
+ {
65
+ if(browserType.substring(s,i) < 412)
66
+ {
67
+ alert(alertMessage);
68
+ }
69
+ }
70
+
71
+
72
+ if(iFirefox > -1)
73
+ {
74
+ if(browserType.substring(i,s) < 2.0)
75
+ {
76
+ alert(alertMessage);
77
+ }
78
+ }
79
+
80
+
81
+ if(iIE > -1)
82
+ {
83
+ temp=navigator.userAgent.split("MSIE")
84
+ version=parseFloat(temp[1])
85
+ //alert(version);
86
+ if(version < 6.0 || iMac > 1)
87
+ {
88
+ alert(alertMessage);
89
+ }
90
+ }
91
+
92
+ }
93
+ }
94
+ }
95
+
96
+
97
+
98
+ </script>
99
+
100
+
101
+ </head>
102
+ <body onLoad="browserAlert()">
103
+
104
+ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
105
+ "http://www.w3.org/TR/html4/loose.dtd">
106
+ <html>
107
+ <head>
108
+ <title>Piano-Sign In</title>
109
+ <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
110
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
111
+ <meta http-equiv="pics-label" content='(pics-1.1 "http://www.icra.org/ratingsv02.html" l gen true for "http://www.apple.com" r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true for "http://www.apple.com" r (n 0 s 0 v 0 l 0))'>
112
+ <meta http-equiv="Expires" content="Fri, 26 Mar 1999 23:59:59 GMT">
113
+ <meta name="Author" content="Apple Computer, Inc.">
114
+
115
+ <link rel="home" href="http://www.apple.com/">
116
+ <link rel="index" href="http://www.apple.com/find/sitemap.html">
117
+
118
+
119
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/index.css" media="screen">
120
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/global.css" media="screen">
121
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/fonts.css" media="screen">
122
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/Pianonav.css" media="screen">
123
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/job.css" media="screen">
124
+ <link rel="stylesheet" type="text/css" href="/WebObjects/piano.woa/Contents/WebServerResources/build.css" media="print">
125
+
126
+ <SCRIPT LANGUAGE="javascript">
127
+
128
+ appleOver = new Image()
129
+ appleOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/AppleOr.png"
130
+
131
+ storeOver = new Image()
132
+ storeOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/StoreOr.png"
133
+
134
+ macOver = new Image()
135
+ macOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/MacOr.png"
136
+
137
+ tunesOver = new Image()
138
+ tunesOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png"
139
+
140
+ phoneOver = new Image()
141
+ phoneOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/PhoneOr.png"
142
+
143
+ downloadOver = new Image()
144
+ downloadOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/DownloadOr.png"
145
+
146
+ supportOver = new Image()
147
+ supportOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/SupportOr.png"
148
+
149
+ originalOver = new Image()
150
+ originalOver.src = "/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png"
151
+
152
+
153
+
154
+
155
+ function original() {
156
+ document.emp.src = originalOver.src; return true;
157
+ }
158
+
159
+
160
+ function apple() {
161
+ document.emp.src = appleOver.src; return true;
162
+ }
163
+
164
+ function store() {
165
+ document.emp.src = storeOver.src; return true;
166
+ }
167
+
168
+ function mac() {
169
+ document.emp.src = macOver.src; return true;
170
+ }
171
+
172
+ function tunes() {
173
+ document.emp.src = tunesOver.src; return true;
174
+ }
175
+
176
+ function phone() {
177
+ document.emp.src = phoneOver.src; return true;
178
+ }
179
+
180
+ function download() {
181
+ document.emp.src = downloadOver.src; return true;
182
+ }
183
+
184
+ function support() {
185
+ document.emp.src = supportOver.src; return true;
186
+ }
187
+
188
+
189
+ </SCRIPT>
190
+
191
+
192
+ </head>
193
+ <body marginwidth=0 marginheight=0 topmargin=0 leftmargin=0 bgcolor="#FFFFFF" background="">
194
+
195
+ <br>
196
+ <IMG NAME="emp" SRC="/WebObjects/piano.woa/Contents/WebServerResources/Images_Header/TunesOr.png" USEMAP="#map1" border = 0>
197
+
198
+
199
+
200
+ <MAP NAME="map1">
201
+
202
+ <AREA
203
+ HREF="http://www.apple.com/" ALT="Apple" TITLE="Apple"
204
+ SHAPE=RECT COORDS="0,0,116,190" onMouseOver="apple()" onMouseOut="original()" onClick= "appleMouseClick()">
205
+ <AREA
206
+ HREF="http://store.apple.com/" ALT="Store" TITLE="Store"
207
+ SHAPE=RECT COORDS="0,0,233,190" onMouseOver="store()" onMouseOut="original()" onClick= "zoomout1()">
208
+ <AREA
209
+ HREF="http://www.apple.com/mac/" ALT="Mac" TITLE="Mac"
210
+ SHAPE=RECT COORDS="0,0,348,190" onMouseOver="mac()" onMouseOut="original()" onClick= "zoomout()">
211
+ <AREA
212
+ HREF="http://www.apple.com/itunes/" ALT="iTunes" TITLE="iTunes"
213
+ SHAPE=RECT COORDS="0,0,465,190" onMouseOver="tunes()" onMouseOut="original()" onClick= "zoomout()">
214
+
215
+ <AREA
216
+ HREF="http://www.apple.com/iphone/" ALT="iPhone" TITLE="iPhone"
217
+ SHAPE=RECT COORDS="0,0,583,190" onMouseOver="phone()" onMouseOut="original()" onClick= "zoomout()">
218
+
219
+ <AREA
220
+ HREF="http://www.apple.com/downloads/" ALT="Downloads" TITLE="Downloads"
221
+ SHAPE=RECT COORDS="0,0,699,190" onMouseOver="download()" onMouseOut="original()" onClick= "zoomout()">
222
+
223
+ <AREA
224
+ HREF="http://www.apple.com/support/" ALT="Support" TITLE="Support"
225
+ SHAPE=RECT COORDS="0,0,817,190" onMouseOver="support()" onMouseOut="original()" onClick= "zoomout()"></MAP>
226
+ </body>
227
+ </html>
228
+
229
+
230
+
231
+
232
+ <input Id="checkAlrt" type="hidden" name="2" />
233
+ <form method="post" action="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.4">
234
+ <br>
235
+ <table>
236
+ <tr>
237
+ <td class="cellLeft">
238
+ <h2><p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="https://phobos.apple.com/images/labelconnect/txt_title_itunesvips.gif" alt="iTunes VIPs" border="0"></p></h2>
239
+ <font size = 2 color = "red"><p align = "center"> </p></font><br>
240
+ <div id="main"><table cellspacing="0" cellpadding="1" width="100%" border="0">
241
+ <tr>
242
+ <td></td>
243
+ <td align="rignt">
244
+ <div id="jobHeader">Sign In</div></td></tr><tr><td valign="top" width="50%"><hr><br><font size=2.5><b>Welcome!</b></font><font size=2>This site will allow you to view and/or download daily and weekly iTunes sales data. <br><br>If you have any issues, please contact us by email at &nbsp;</font><font size=2.5><b><a href= mailto:itunesondemand@apple.com>itunesondemand@apple.com<br><br></a></b><p>Please note: The information you are accessing on this site is market trend reports, and should not be considered to be your monthly royalty reports.<br></p><font><p></p><hr></td><td valign="top">
245
+ <div id="DSSecurityList">
246
+ <table>
247
+ <tr>
248
+ <td colspan="3">
249
+ <font size="1" color=red><a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.4.18"></a></font>
250
+ </td>
251
+ </tr>
252
+ <tr>
253
+ <td colspan="3">
254
+
255
+ <script>
256
+ function dsfocus(){ document.appleConnectForm.theAccountName.focus(); }
257
+ </script>
258
+
259
+
260
+
261
+
262
+ <!-- This FORM is embedded inside another FORM. Omitting Open Tag -->
263
+
264
+ <div id="ds_container">
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+
274
+ <font face="Lucida Grande, Geneva, Verdana, Arial, Helvetica, sans-serif" CLASS="L12" />
275
+ <font size=2>
276
+ <span class="dstext">Please enter your Apple ID and password.</span>
277
+ </font>
278
+ </font /><br><br>
279
+
280
+
281
+
282
+
283
+
284
+
285
+
286
+
287
+
288
+
289
+ <table cellpadding=0 cellspacing=0 border=0 width=273>
290
+ <tr valign=top>
291
+ <td align=left><font size=2><font face="Geneva, Verdana, Arial, Helvetica, sans-serif" SIZE=1 CLASS="G10" />
292
+ <label for="accountname"><span class="dslabel">Apple ID</span></label>
293
+ <font color="#ff1102"></font></font /><br>
294
+ <font size='2'><input size="30" maxlength="128" id="accountname" type="text" name="theAccountName" /></font></font>
295
+
296
+ </td>
297
+ </tr>
298
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
299
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
300
+ <tr>
301
+ <td align='left'>
302
+ <font face="Geneva, Verdana, Arial, Helvetica, sans-serif" SIZE=1 CLASS="G10" />
303
+ <label for="accountpassword"><span class="dslabel">Password</span></label>
304
+ </font />
305
+ <br>
306
+ <input size="30" maxlength="32" id="accountpassword" type="password" name="theAccountPW" /><input border="0" width="0" height="0" type="image" name="1.Continue" src="/AppleConnect/US-EN/spacer.gif" />
307
+ </td>
308
+ </tr>
309
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
310
+ <tr><td><img alt="" width="273" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td></tr>
311
+ <tr>
312
+ <td>
313
+ <table cellspacing='0' border='0' width='273' cellpadding='0'>
314
+ <tr align='left'>
315
+ <td align='left' width='170'>
316
+
317
+
318
+
319
+ <font size='1'>
320
+ <span class="dstext">
321
+
322
+
323
+
324
+
325
+
326
+
327
+ <input border="0" alt="Did you forget your password?" type="image" name="1.Forgot" src="/AppleConnect/US-EN/forgotpasswdlink.gif" />
328
+
329
+
330
+ </span>
331
+ </font>
332
+
333
+ </td>
334
+ <td width='15'><img alt="" width="15" height="5" src="/AppleConnect/US-EN/spacer.gif" /></td>
335
+ <td align='left' width='88'><input border="0" alt="Sign In" type="image" name="1.Continue" src="/AppleConnect/US-EN/buttonloginlime.gif" /></td>
336
+ </tr>
337
+ </table>
338
+ </td>
339
+ </tr>
340
+ </table>
341
+
342
+
343
+
344
+
345
+
346
+ <input type="hidden" name="theAuxValue" />
347
+ </div>
348
+ <input type="hidden" name="wosid" value="9tkH3ucxG921Y1uBDoPAIM" />
349
+ <!-- This FORM is embedded inside another FORM. Omitting Close Tag -->
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+
359
+ <p></p><br><br></td>
360
+ </tr>
361
+ </table>
362
+ </td></tr></table></div><div class="sidebar"></div><div class="clear"></div><input type="hidden" name="wosid" value="9tkH3ucxG921Y1uBDoPAIM" /></form><br> <div class="footerLinks" align="center"><a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.1">Home</a> <!--| <a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.3">FAQs</a> -->| <a href="/cgi-bin/WebObjects/Piano.woa/2/wo/9tkH3ucxG921Y1uBDoPAIM/0.6.5">Sign Out</a></div>
363
+ <div class="footerCopyright" align="center">Copyright &#169; 2008 Apple Inc. All rights reserved. </div><br/>
364
+ </body>
365
+ </html>