6e53f9819c3fcddd2bc1c21324705037556ef5ef
[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 <boost/asio.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/thread.hpp>
24 #include "json_server.h"
25 #include "job_manager.h"
26 #include "job.h"
27 #include "util.h"
28 #include "film.h"
29 #include "transcode_job.h"
30 #include <boost/make_shared.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::make_shared;
40 using boost::dynamic_pointer_cast;
41 using boost::asio::ip::tcp;
42
43 #define MAX_LENGTH 512
44
45 enum State {
46         AWAITING_G,
47         AWAITING_E,
48         AWAITING_T,
49         AWAITING_SPACE,
50         READING_URL,
51 };
52
53 JSONServer::JSONServer (int port)
54 {
55         new thread (boost::bind (&JSONServer::run, this, port));
56 }
57
58 void
59 JSONServer::run (int port)
60 try
61 {
62         boost::asio::io_service io_service;
63         tcp::acceptor a (io_service, tcp::endpoint (tcp::v4 (), port));
64         while (true) {
65                 try {
66                         shared_ptr<tcp::socket> s = make_shared<tcp::socket> (io_service);
67                         a.accept (*s);
68                         handle (s);
69                 }
70                 catch (...) {
71
72                 }
73         }
74 }
75 catch (...)
76 {
77
78 }
79
80 void
81 JSONServer::handle (shared_ptr<tcp::socket> socket)
82 {
83         string url;
84         State state = AWAITING_G;
85
86         while (true) {
87                 char data[MAX_LENGTH];
88                 boost::system::error_code error;
89                 size_t len = socket->read_some (boost::asio::buffer (data), error);
90                 if (error) {
91                         cout << "error.\n";
92                         break;
93                 }
94
95                 char* p = data;
96                 char* e = data + len;
97                 while (p != e) {
98
99                         State old_state = state;
100                         switch (state) {
101                         case AWAITING_G:
102                                 if (*p == 'G') {
103                                         state = AWAITING_E;
104                                 }
105                                 break;
106                         case AWAITING_E:
107                                 if (*p == 'E') {
108                                         state = AWAITING_T;
109                                 }
110                                 break;
111                         case AWAITING_T:
112                                 if (*p == 'T') {
113                                         state = AWAITING_SPACE;
114                                 }
115                                 break;
116                         case AWAITING_SPACE:
117                                 if (*p == ' ') {
118                                         state = READING_URL;
119                                 }
120                                 break;
121                         case READING_URL:
122                                 if (*p == ' ') {
123                                         request (url, socket);
124                                         state = AWAITING_G;
125                                         url = "";
126                                 } else {
127                                         url += *p;
128                                 }
129                                 break;
130                         }
131
132                         if (state == old_state && state != READING_URL) {
133                                 state = AWAITING_G;
134                         }
135
136                         ++p;
137                 }
138         }
139 }
140
141 void
142 JSONServer::request (string url, shared_ptr<tcp::socket> socket)
143 {
144         cout << "request: " << url << "\n";
145
146         map<string, string> r = split_get_request (url);
147         for (map<string, string>::iterator i = r.begin(); i != r.end(); ++i) {
148                 cout << i->first << " => " << i->second << "\n";
149         }
150
151         string action;
152         if (r.find ("action") != r.end ()) {
153                 action = r["action"];
154         }
155
156         SafeStringStream json;
157         if (action == "status") {
158
159                 list<shared_ptr<Job> > jobs = JobManager::instance()->get ();
160
161                 json << "{ \"jobs\": [";
162                 for (list<shared_ptr<Job> >::iterator i = jobs.begin(); i != jobs.end(); ++i) {
163
164                         json << "{ ";
165
166                         if ((*i)->film()) {
167                                 json << "\"dcp\": \"" << (*i)->film()->dcp_name() << "\", ";
168                         }
169
170                         json << "\"name\": \""   << (*i)->json_name() << "\", ";
171                         if ((*i)->progress ()) {
172                                 json << "\"progress\": " << (*i)->progress().get() << ", ";
173                         } else {
174                                 json << "\"progress\": unknown, ";
175                         }
176                         json << "\"status\": \"" << (*i)->json_status() << "\"";
177                         json << " }";
178
179                         list<shared_ptr<Job> >::iterator j = i;
180                         ++j;
181                         if (j != jobs.end ()) {
182                                 json << ", ";
183                         }
184                 }
185                 json << "] }";
186
187                 if (json.str().empty ()) {
188                         json << "{ }";
189                 }
190         }
191
192         SafeStringStream reply;
193         reply << "HTTP/1.1 200 OK\r\n"
194               << "Content-Length: " << json.str().length() << "\r\n"
195               << "Content-Type: application/json\r\n"
196                               << "\r\n"
197               << json.str () << "\r\n";
198         cout << "reply: " << json.str() << "\n";
199         boost::asio::write (*socket, boost::asio::buffer (reply.str().c_str(), reply.str().length()));
200 }