490f9fc5f6b211321c93b2609acbdc8924901c0f
[dcpomatic.git] / src / lib / json_server.cc
1 /*
2     Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "json_server.h"
22 #include "job_manager.h"
23 #include "job.h"
24 #include "util.h"
25 #include "film.h"
26 #include "transcode_job.h"
27 #include "raw_convert.h"
28 #include <boost/asio.hpp>
29 #include <boost/bind.hpp>
30 #include <boost/thread.hpp>
31 #include <iostream>
32
33 using std::string;
34 using std::cout;
35 using std::map;
36 using std::list;
37 using boost::thread;
38 using boost::shared_ptr;
39 using boost::dynamic_pointer_cast;
40 using boost::asio::ip::tcp;
41
42 #define MAX_LENGTH 512
43
44 enum State {
45         AWAITING_G,
46         AWAITING_E,
47         AWAITING_T,
48         AWAITING_SPACE,
49         READING_URL,
50 };
51
52 JSONServer::JSONServer (int port)
53 {
54         new thread (boost::bind (&JSONServer::run, this, port));
55 }
56
57 void
58 JSONServer::run (int port)
59 try
60 {
61         boost::asio::io_service io_service;
62         tcp::acceptor a (io_service, tcp::endpoint (tcp::v4 (), port));
63         while (true) {
64                 try {
65                         shared_ptr<tcp::socket> s (new tcp::socket (io_service));
66                         a.accept (*s);
67                         handle (s);
68                 }
69                 catch (...) {
70
71                 }
72         }
73 }
74 catch (...)
75 {
76
77 }
78
79 void
80 JSONServer::handle (shared_ptr<tcp::socket> socket)
81 {
82         string url;
83         State state = AWAITING_G;
84
85         while (true) {
86                 char data[MAX_LENGTH];
87                 boost::system::error_code error;
88                 size_t len = socket->read_some (boost::asio::buffer (data), error);
89                 if (error) {
90                         cout << "error.\n";
91                         break;
92                 }
93
94                 char* p = data;
95                 char* e = data + len;
96                 while (p != e) {
97
98                         State old_state = state;
99                         switch (state) {
100                         case AWAITING_G:
101                                 if (*p == 'G') {
102                                         state = AWAITING_E;
103                                 }
104                                 break;
105                         case AWAITING_E:
106                                 if (*p == 'E') {
107                                         state = AWAITING_T;
108                                 }
109                                 break;
110                         case AWAITING_T:
111                                 if (*p == 'T') {
112                                         state = AWAITING_SPACE;
113                                 }
114                                 break;
115                         case AWAITING_SPACE:
116                                 if (*p == ' ') {
117                                         state = READING_URL;
118                                 }
119                                 break;
120                         case READING_URL:
121                                 if (*p == ' ') {
122                                         request (url, socket);
123                                         state = AWAITING_G;
124                                         url = "";
125                                 } else {
126                                         url += *p;
127                                 }
128                                 break;
129                         }
130
131                         if (state == old_state && state != READING_URL) {
132                                 state = AWAITING_G;
133                         }
134
135                         ++p;
136                 }
137         }
138 }
139
140 void
141 JSONServer::request (string url, shared_ptr<tcp::socket> socket)
142 {
143         cout << "request: " << url << "\n";
144
145         map<string, string> r = split_get_request (url);
146         for (map<string, string>::iterator i = r.begin(); i != r.end(); ++i) {
147                 cout << i->first << " => " << i->second << "\n";
148         }
149
150         string action;
151         if (r.find ("action") != r.end ()) {
152                 action = r["action"];
153         }
154
155         string json;
156         if (action == "status") {
157
158                 list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
159
160                 json += "{ \"jobs\": [";
161                 for (list<shared_ptr<Job> >::iterator i = jobs.begin(); i != jobs.end(); ++i) {
162
163                         json += "{ ";
164
165                         if ((*i)->film()) {
166                                 json += "\"dcp\": \"" + (*i)->film()->dcp_name() + "\", ";
167                         }
168
169                         json += "\"name\": \"" + (*i)->json_name() + "\", ";
170                         if ((*i)->progress ()) {
171                                 json += "\"progress\": " + raw_convert<string>((*i)->progress().get()) + ", ";
172                         } else {
173                                 json += "\"progress\": unknown, ";
174                         }
175                         json += "\"status\": \"" + (*i)->json_status() + "\"";
176                         json += " }";
177
178                         list<shared_ptr<Job> >::iterator j = i;
179                         ++j;
180                         if (j != jobs.end ()) {
181                                 json += ", ";
182                         }
183                 }
184                 json += "] }";
185         }
186
187         string reply = "HTTP/1.1 200 OK\r\n"
188                 "Content-Length: " + raw_convert<string>(json.length()) + "\r\n"
189                 "Content-Type: application/json\r\n"
190                 "\r\n"
191                 + json + "\r\n";
192         cout << "reply: " << json << "\n";
193         boost::asio::write (*socket, boost::asio::buffer (reply.c_str(), reply.length()));
194 }