1// Copyright 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 "cloud_print/gcp20/prototype/cloud_print_response_parser.h"
6
7#include "base/json/json_reader.h"
8#include "base/json/json_writer.h"
9#include "base/logging.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/values.h"
12
13namespace cloud_print_response_parser {
14
15Job::Job() {
16}
17
18Job::~Job() {
19}
20
21// Parses json base::Value to base::DictionaryValue.
22// If |success| value in JSON dictionary is |false| then |message| will
23// contain |message| from the JSON dictionary.
24// Returns |true| if no parsing error occurred.
25bool GetJsonDictinaryAndCheckSuccess(base::Value* json,
26                                     std::string* error_description,
27                                     bool* json_success,
28                                     std::string* message,
29                                     base::DictionaryValue** dictionary) {
30  base::DictionaryValue* response_dictionary = NULL;
31  if (!json || !json->GetAsDictionary(&response_dictionary)) {
32    *error_description = "No JSON dictionary response received.";
33    return false;
34  }
35
36  bool response_json_success = false;
37  if (!response_dictionary->GetBoolean("success", &response_json_success)) {
38    *error_description = "Cannot parse success state.";
39    return false;
40  }
41
42  if (!response_json_success) {
43    std::string response_message;
44    if (!response_dictionary->GetString("message", &response_message)) {
45      *error_description = "Cannot parse message from response.";
46      return false;
47    }
48    *message = response_message;
49  }
50
51  *json_success = response_json_success;
52  *dictionary = response_dictionary;
53  return true;
54}
55
56bool ParseRegisterStartResponse(const std::string& response,
57                                std::string* error_description,
58                                std::string* polling_url,
59                                std::string* registration_token,
60                                std::string* complete_invite_url,
61                                std::string* device_id) {
62  scoped_ptr<base::Value> json(base::JSONReader::Read(response));
63  base::DictionaryValue* response_dictionary = NULL;
64  bool json_success;
65  std::string message;
66  if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
67                                       &json_success, &message,
68                                       &response_dictionary)) {
69    return false;
70  }
71  if (!json_success) {
72    *error_description = message;
73    return false;
74  }
75
76  std::string response_registration_token;
77  if (!response_dictionary->GetString("registration_token",
78                                      &response_registration_token)) {
79    *error_description = "No registration_token specified.";
80    return false;
81  }
82
83  std::string response_complete_invite_url;
84  if (!response_dictionary->GetString("complete_invite_url",
85                                      &response_complete_invite_url)) {
86    *error_description = "No complete_invite_url specified.";
87    return false;
88  }
89
90  std::string response_polling_url;
91  if (!response_dictionary->GetString("polling_url", &response_polling_url)) {
92    *error_description = "No polling_url specified.";
93    return false;
94  }
95
96  base::ListValue* list = NULL;
97  if (!response_dictionary->GetList("printers", &list)) {
98    *error_description = "No printers list specified.";
99    return false;
100  }
101
102  base::DictionaryValue* printer = NULL;
103  if (!list->GetDictionary(0, &printer)) {
104    *error_description = "Printers list is empty or printer is not dictionary.";
105    return false;
106  }
107
108  std::string id;
109  if (!printer->GetString("id", &id)) {
110    *error_description = "No id specified.";
111    return false;
112  }
113
114  if (id.empty()) {
115    *error_description = "id is empty.";
116    return false;
117  }
118
119  *polling_url = response_polling_url;
120  *registration_token = response_registration_token;
121  *complete_invite_url = response_complete_invite_url;
122  *device_id = id;
123  return true;
124}
125
126bool ParseRegisterCompleteResponse(const std::string& response,
127                                   std::string* error_description,
128                                   std::string* authorization_code,
129                                   std::string* xmpp_jid) {
130  scoped_ptr<base::Value> json(base::JSONReader::Read(response));
131  base::DictionaryValue* response_dictionary = NULL;
132  bool json_success;
133  std::string message;
134  if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
135                                       &json_success, &message,
136                                       &response_dictionary)) {
137    return false;
138  }
139  if (!json_success) {
140    *error_description = message;
141    return false;
142  }
143
144  std::string response_authorization_code;
145  if (!response_dictionary->GetString("authorization_code",
146                                      &response_authorization_code)) {
147    *error_description = "Cannot parse authorization_code.";
148    return false;
149  }
150
151  std::string response_xmpp_jid;
152  if (!response_dictionary->GetString("xmpp_jid", &response_xmpp_jid)) {
153    *error_description = "Cannot parse xmpp jid.";
154    return false;
155  }
156
157  *authorization_code = response_authorization_code;
158  *xmpp_jid = response_xmpp_jid;
159  return true;
160}
161
162bool ParseFetchResponse(const std::string& response,
163                        std::string* error_description,
164                        std::vector<Job>* list) {
165  scoped_ptr<base::Value> json(base::JSONReader::Read(response));
166  base::DictionaryValue* response_dictionary = NULL;
167  bool json_success;
168  std::string message;
169  if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
170                                       &json_success, &message,
171                                       &response_dictionary)) {
172    return false;
173  }
174
175  if (!json_success) {  // Let's suppose we have no jobs to proceed.
176    list->clear();
177    return true;
178  }
179
180  base::ListValue* jobs = NULL;
181  if (!response_dictionary->GetList("jobs", &jobs)) {
182    *error_description = "Cannot parse jobs list.";
183    return false;
184  }
185
186  std::vector<Job> job_list(jobs->GetSize());
187  std::string create_time_str;
188  for (size_t idx = 0; idx < job_list.size(); ++idx) {
189    base::DictionaryValue* job = NULL;
190    jobs->GetDictionary(idx, &job);
191    if (!job->GetString("id", &job_list[idx].job_id) ||
192        !job->GetString("createTime", &create_time_str) ||
193        !job->GetString("fileUrl", &job_list[idx].file_url) ||
194        !job->GetString("ticketUrl", &job_list[idx].ticket_url) ||
195        !job->GetString("title", &job_list[idx].title)) {
196      *error_description = "Cannot parse job info.";
197      return false;
198    }
199    int64 create_time_ms = 0;
200    if (!base::StringToInt64(create_time_str, &create_time_ms)) {
201      *error_description = "Cannot convert time.";
202      return false;
203    }
204    job_list[idx].create_time =
205        base::Time::UnixEpoch() +
206        base::TimeDelta::FromMilliseconds(create_time_ms);
207  }
208
209  *list = job_list;
210  return true;
211}
212
213bool ParseLocalSettingsResponse(const std::string& response,
214                                std::string* error_description,
215                                LocalSettings::State* state,
216                                LocalSettings* settings) {
217  scoped_ptr<base::Value> json(base::JSONReader::Read(response));
218  base::DictionaryValue* response_dictionary = NULL;
219  bool json_success;
220  std::string message;
221  if (!GetJsonDictinaryAndCheckSuccess(json.get(), error_description,
222                                       &json_success, &message,
223                                       &response_dictionary)) {
224    return false;
225  }
226
227  if (!json_success) {  // Let's suppose our printer was deleted.
228    *state = LocalSettings::PRINTER_DELETED;
229    return true;
230  }
231
232  base::ListValue* list = NULL;
233  if (!response_dictionary->GetList("printers", &list)) {
234    *error_description = "No printers list specified.";
235    return false;
236  }
237
238  base::DictionaryValue* printer = NULL;
239  if (!list->GetDictionary(0, &printer)) {
240    *error_description = "Printers list is empty or printer is not dictionary.";
241    return false;
242  }
243
244  base::DictionaryValue* local_settings_dict = NULL;
245  if (!printer->GetDictionary("local_settings", &local_settings_dict)) {
246    *error_description = "No local_settings found.";
247    return false;
248  }
249
250  base::DictionaryValue* current = NULL;
251  if (!local_settings_dict->GetDictionary("current", &current)) {
252    *error_description = "No *current* local settings found.";
253    return false;
254  }
255
256  LocalSettings::State settings_state;
257  base::DictionaryValue* pending = NULL;
258  base::DictionaryValue* settings_to_parse = NULL;
259  if (local_settings_dict->GetDictionary("pending", &pending)) {
260    settings_to_parse = pending;
261    settings_state = LocalSettings::PENDING;
262  } else {
263    settings_to_parse = current;
264    settings_state = LocalSettings::CURRENT;
265  }
266
267  LocalSettings local_settings;
268  if (!settings_to_parse->GetBoolean("local_discovery",
269                                     &local_settings.local_discovery) ||
270      !settings_to_parse->GetBoolean("access_token_enabled",
271                                     &local_settings.access_token_enabled) ||
272      !settings_to_parse->GetBoolean("printer/local_printing_enabled",
273                                     &local_settings.local_printing_enabled) ||
274      !settings_to_parse->GetInteger("xmpp_timeout_value",
275                                     &local_settings.xmpp_timeout_value)) {
276    *error_description = "Cannot parse local_settings info.";
277    return false;
278  }
279
280  *state = settings_state;
281  *settings = local_settings;
282  return true;
283}
284
285}  // namespace cloud_print_response_parser
286
287