blob: 5edcc4dd285b45e64df4f00cc82699d7f3a7bc50 [file] [log] [blame]
Dennis Kempincee37612013-06-14 10:40:41 -07001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import cookielib
6import getpass
7import imghdr
8import os
9import os.path
Dennis Kempin13d948e2014-04-18 11:23:32 -070010import re
Charlie Mooneyd30322b2014-09-04 15:02:29 -070011import subprocess
Dennis Kempincee37612013-06-14 10:40:41 -070012import sys
13import urllib
14import urllib2
15
16# path to current script directory
17script_dir = os.path.dirname(os.path.realpath(__file__))
18cache_dir = os.path.realpath(os.path.join(script_dir, "..", "cache"))
19if not os.path.exists(cache_dir):
20 os.mkdir(cache_dir)
21
22# path to the cookies file used for storing the login cookies.
23cookies_file = os.path.join(cache_dir, "cookies")
24
25# path to folder where downloaded reports are cached
26log_cache_dir = os.path.join(cache_dir, "reports")
27if not os.path.exists(log_cache_dir):
28 os.mkdir(log_cache_dir)
29
30class FeedbackDownloader():
Charlie Mooneyd30322b2014-09-04 15:02:29 -070031 STUBBY_CMD = 'stubby --security_protocol=loas --binary_output=0 \
32 call blade:feedback-export-api-prod \
33 ReportService.Get \'resource_id: "{}"\''
34 BINARY_DATA_FILTER = '| grep -A 2 \'name: "system_logs.zip"\' | tail -n 1 \
35 | sed \'s/.*data: "\(.*\)"\s*/\\1/\''
36 SCREENSHOT_FILTER = '| grep -A 2 \'screenshot <\' | tail -n 1 \
37 | sed \'s/.*content: "\(.*\)"\s*/\\1/\''
38
Dennis Kempin13d948e2014-04-18 11:23:32 -070039 def __init__(self, force_login=False):
40 if force_login and os.path.exists(cookies_file):
41 os.remove(cookies_file)
Charlie Mooneyd30322b2014-09-04 15:02:29 -070042 self._SetupCookies()
43
44 def _SetupCookies(self):
Dennis Kempincee37612013-06-14 10:40:41 -070045 # setup cookies file and url opener
46 self.cookies = cookielib.MozillaCookieJar(cookies_file)
47 if os.path.exists(cookies_file):
48 self.cookies.load()
49 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
50 self.opener = urllib2.build_opener(cookie_processor)
51 self._Login()
52
53 def _Login(self):
54 username = getpass.getuser()
55
56 # check if already logged in
Dennis Kempin13d948e2014-04-18 11:23:32 -070057 url = "https://feedback.corp.google.com"
Dennis Kempincee37612013-06-14 10:40:41 -070058 data = self.opener.open(url).read()
Dennis Kempin13d948e2014-04-18 11:23:32 -070059 if "loginForm" not in data:
Dennis Kempincee37612013-06-14 10:40:41 -070060 return
61
62 # ask for credentials
63 print "Login to corp.google.com:"
64 password = getpass.getpass()
65 sys.stdout.write("OTP: ")
66 otp = sys.stdin.readline().strip()
67
Dennis Kempin13d948e2014-04-18 11:23:32 -070068 values = {
69 'u': username,
70 'pw': password,
71 'otp': otp
72 }
Dennis Kempincee37612013-06-14 10:40:41 -070073
Dennis Kempin13d948e2014-04-18 11:23:32 -070074 # extract hidden form values
75 regex = ("<input type=\"hidden\" id=\"([^\"]*)\" " +
76 "name=\"([^\"]*)\" value=\"([^\"]*)\"/>")
77 for match in re.finditer(regex, data):
78 values[match.group(2)] = match.group(3)
79
80 # execute login by posting userdata to login.corp.google.com
81 query = urllib.urlencode(values)
82 url = ("https://login.corp.google.com/login")
83 result = self.opener.open(url, query).read()
Dennis Kempincee37612013-06-14 10:40:41 -070084 # check if the result displays error
85 if "error" in result >= 0:
86 print "Login failed"
87 exit(-1)
88
89 # login was successful. save cookies to file to be reused later.
90 self.cookies.save()
91
92 def DownloadFile(self, url):
Charlie Mooneyd30322b2014-09-04 15:02:29 -070093 self._SetupCookies()
Dennis Kempincee37612013-06-14 10:40:41 -070094 try:
95 return self.opener.open(url).read()
96 except urllib2.URLError:
97 return None
98
Charlie Mooneyd30322b2014-09-04 15:02:29 -070099 def _OctetStreamToBinary(self, octetStream):
100 """ The zip files are returned in an octet-stream format that must
101 be decoded back into a binary. This function scans through the stream
102 and unescapes the special characters
103 """
104 binary = ''
105 i = 0
106 while i < len(octetStream):
107 if ord(octetStream[i]) is ord('\\'):
108 if re.match('\d\d\d', octetStream[i + 1:i + 4]):
109 binary += chr(int(octetStream[i + 1:i + 4], 8))
110 i += 4
111 else:
112 binary += octetStream[i:i + 2].decode("string-escape")
113 i += 2
114 else:
115 binary += octetStream[i]
116 i += 1
117 return binary
118
119 def _DownloadAttachedFile(self, id, filter_cmd):
120 cmd = FeedbackDownloader.STUBBY_CMD.format(id) + filter_cmd
121 print cmd
122 process = subprocess.Popen(cmd, shell=True,
123 stdout=subprocess.PIPE,
124 stderr=subprocess.PIPE)
125
126 output, errors = process.communicate()
127 errorcode = process.returncode
128 if errorcode != 0:
129 print "An error (%d) occurred while downloading the logs" % errorcode
Dennis Kempincee37612013-06-14 10:40:41 -0700130 return None
Charlie Mooneyd30322b2014-09-04 15:02:29 -0700131
132 return self._OctetStreamToBinary(output)
Dennis Kempincee37612013-06-14 10:40:41 -0700133
134 def DownloadSystemLog(self, id):
Charlie Mooneyd30322b2014-09-04 15:02:29 -0700135 report = self._DownloadAttachedFile(id,
136 FeedbackDownloader.BINARY_DATA_FILTER)
Dennis Kempinc6c981d2014-04-18 11:49:16 -0700137
Dennis Kempincee37612013-06-14 10:40:41 -0700138 if not report or (report[0:2] != "BZ" and report[0:2] != "PK"):
Charlie Mooneyd30322b2014-09-04 15:02:29 -0700139 print "Report does not seem to include include log files..."
140 print "Do you need to run 'prodaccess' perhaps?"
Dennis Kempincee37612013-06-14 10:40:41 -0700141 return None
Charlie Mooneyd30322b2014-09-04 15:02:29 -0700142
Dennis Kempincee37612013-06-14 10:40:41 -0700143 return report
144
145 def DownloadScreenshot(self, id):
Charlie Mooneyd30322b2014-09-04 15:02:29 -0700146 return self._DownloadAttachedFile(id, FeedbackDownloader.SCREENSHOT_FILTER)