1// Copyright (c) 2013 The Chromium 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
5#include "chrome/browser/net/net_log_temp_file.h"
6
7#include "base/files/file_util.h"
8#include "base/values.h"
9#include "chrome/browser/net/chrome_net_log.h"
10#include "chrome/browser/ui/webui/net_internals/net_internals_ui.h"
11#include "content/public/browser/browser_thread.h"
12#include "net/base/net_log_logger.h"
13
14using content::BrowserThread;
15
16NetLogTempFile::NetLogTempFile(ChromeNetLog* chrome_net_log)
17    : state_(STATE_UNINITIALIZED),
18      log_type_(LOG_TYPE_NONE),
19      log_filename_(FILE_PATH_LITERAL("chrome-net-export-log.json")),
20      chrome_net_log_(chrome_net_log) {
21}
22
23NetLogTempFile::~NetLogTempFile() {
24  if (net_log_logger_)
25    net_log_logger_->StopObserving();
26}
27
28void NetLogTempFile::ProcessCommand(Command command) {
29  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
30  if (!EnsureInit())
31    return;
32
33  switch (command) {
34    case DO_START:
35      StartNetLog(false);
36      break;
37    case DO_START_STRIP_PRIVATE_DATA:
38      StartNetLog(true);
39      break;
40    case DO_STOP:
41      StopNetLog();
42      break;
43    default:
44      NOTREACHED();
45      break;
46  }
47}
48
49base::DictionaryValue* NetLogTempFile::GetState() {
50  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
51  base::DictionaryValue* dict = new base::DictionaryValue;
52
53  EnsureInit();
54
55#ifndef NDEBUG
56  dict->SetString("file", log_path_.LossyDisplayName());
57#endif  // NDEBUG
58
59  switch (state_) {
60    case STATE_NOT_LOGGING:
61      dict->SetString("state", "NOT_LOGGING");
62      break;
63    case STATE_LOGGING:
64      dict->SetString("state", "LOGGING");
65      break;
66    case STATE_UNINITIALIZED:
67      dict->SetString("state", "UNINITIALIZED");
68      break;
69  }
70
71  switch (log_type_) {
72    case LOG_TYPE_NONE:
73      dict->SetString("logType", "NONE");
74      break;
75    case LOG_TYPE_UNKNOWN:
76      dict->SetString("logType", "UNKNOWN");
77      break;
78    case LOG_TYPE_NORMAL:
79      dict->SetString("logType", "NORMAL");
80      break;
81    case LOG_TYPE_STRIP_PRIVATE_DATA:
82      dict->SetString("logType", "STRIP_PRIVATE_DATA");
83      break;
84  }
85
86  return dict;
87}
88
89bool NetLogTempFile::EnsureInit() {
90  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
91  if (state_ != STATE_UNINITIALIZED)
92    return true;
93
94  if (!GetNetExportLog())
95    return false;
96
97  state_ = STATE_NOT_LOGGING;
98  if (NetExportLogExists())
99    log_type_ = LOG_TYPE_UNKNOWN;
100  else
101    log_type_ = LOG_TYPE_NONE;
102
103  return true;
104}
105
106void NetLogTempFile::StartNetLog(bool strip_private_data) {
107  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
108  if (state_ == STATE_LOGGING)
109    return;
110
111  DCHECK_NE(STATE_UNINITIALIZED, state_);
112  DCHECK(!log_path_.empty());
113
114  // Try to make sure we can create the file.
115  // TODO(rtenneti): Find a better for doing the following. Surface some error
116  // to the user if we couldn't create the file.
117  FILE* file = base::OpenFile(log_path_, "w");
118  if (file == NULL)
119    return;
120
121  scoped_ptr<base::Value> constants(NetInternalsUI::GetConstants());
122  net_log_logger_.reset(new net::NetLogLogger(file, *constants));
123  if (strip_private_data) {
124    net_log_logger_->set_log_level(net::NetLog::LOG_STRIP_PRIVATE_DATA);
125    log_type_ = LOG_TYPE_STRIP_PRIVATE_DATA;
126  } else {
127    log_type_ = LOG_TYPE_NORMAL;
128  }
129  net_log_logger_->StartObserving(chrome_net_log_);
130  state_ = STATE_LOGGING;
131}
132
133void NetLogTempFile::StopNetLog() {
134  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
135  if (state_ != STATE_LOGGING)
136    return;
137
138  net_log_logger_->StopObserving();
139  net_log_logger_.reset();
140  state_ = STATE_NOT_LOGGING;
141}
142
143bool NetLogTempFile::GetFilePath(base::FilePath* path) {
144  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
145  if (log_type_ == LOG_TYPE_NONE || state_ == STATE_LOGGING)
146    return false;
147
148  if (!NetExportLogExists())
149    return false;
150
151  DCHECK(!log_path_.empty());
152#if defined(OS_POSIX)
153  // Users, group and others can read, write and traverse.
154  int mode = base::FILE_PERMISSION_MASK;
155  base::SetPosixFilePermissions(log_path_, mode);
156#endif  // defined(OS_POSIX)
157
158  *path = log_path_;
159  return true;
160}
161
162bool NetLogTempFile::GetNetExportLog() {
163  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
164  base::FilePath temp_dir;
165  if (!GetNetExportLogDirectory(&temp_dir))
166    return false;
167
168  log_path_ = temp_dir.Append(log_filename_);
169  return true;
170}
171
172bool NetLogTempFile::GetNetExportLogDirectory(base::FilePath* path) {
173  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
174  return base::GetTempDir(path);
175}
176
177bool NetLogTempFile::NetExportLogExists() {
178  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE_USER_BLOCKING));
179  DCHECK(!log_path_.empty());
180  return base::PathExists(log_path_);
181}
182