1 #include "stdafx.h"
2 #include <iostream>
3 #include "Camera.h"
4 #include "HTMLSanitizer.h"
5 #include "localtime_r.h"
6 #include "Logger.h"
7 #include "Helper.h"
8 #include "mainworker.h"
9 #include "../httpclient/HTTPClient.h"
10 #include "../smtpclient/SMTPClient.h"
11 #include "../webserver/Base64.h"
12 #include "SQLHelper.h"
13 #include "WebServer.h"
14 #include "../webserver/cWebem.h"
15 #include <json/json.h>
16
17 #define CAMERA_POLL_INTERVAL 30
18
19 extern std::string szUserDataFolder;
20
CCameraHandler(void)21 CCameraHandler::CCameraHandler(void)
22 {
23 m_seconds_counter = 0;
24 }
25
~CCameraHandler(void)26 CCameraHandler::~CCameraHandler(void)
27 {
28 }
29
ReloadCameras()30 void CCameraHandler::ReloadCameras()
31 {
32 std::vector<std::string> _AddedCameras;
33 std::lock_guard<std::mutex> l(m_mutex);
34 m_cameradevices.clear();
35 std::vector<std::vector<std::string> > result;
36
37 result = m_sql.safe_query("SELECT ID, Name, Address, Port, Username, Password, ImageURL, Protocol FROM Cameras WHERE (Enabled == 1) ORDER BY ID");
38 if (!result.empty())
39 {
40 _log.Log(LOG_STATUS, "Camera: settings (re)loaded");
41 for (const auto & itt : result)
42 {
43 std::vector<std::string> sd = itt;
44
45 cameraDevice citem;
46 citem.ID = std::stoull(sd[0]);
47 citem.Name = sd[1];
48 citem.Address = sd[2];
49 citem.Port = atoi(sd[3].c_str());
50 citem.Username = base64_decode(sd[4]);
51 citem.Password = base64_decode(sd[5]);
52 citem.ImageURL = sd[6];
53 citem.Protocol = (eCameraProtocol)atoi(sd[7].c_str());
54 m_cameradevices.push_back(citem);
55 _AddedCameras.push_back(sd[0]);
56 }
57 }
58
59 for (const auto & ittCam : _AddedCameras)
60 {
61 //Get Active Devices/Scenes
62 ReloadCameraActiveDevices(ittCam);
63 }
64 }
65
ReloadCameraActiveDevices(const std::string & CamID)66 void CCameraHandler::ReloadCameraActiveDevices(const std::string &CamID)
67 {
68 cameraDevice *pCamera = GetCamera(CamID);
69 if (pCamera == NULL)
70 return;
71 pCamera->mActiveDevices.clear();
72 std::vector<std::vector<std::string> > result;
73 result = m_sql.safe_query("SELECT ID, DevSceneType, DevSceneRowID FROM CamerasActiveDevices WHERE (CameraRowID=='%q') ORDER BY ID", CamID.c_str());
74 if (!result.empty())
75 {
76 for (const auto & itt : result)
77 {
78 std::vector<std::string> sd = itt;
79 cameraActiveDevice aDevice;
80 aDevice.ID = std::stoull(sd[0]);
81 aDevice.DevSceneType = (unsigned char)atoi(sd[1].c_str());
82 aDevice.DevSceneRowID = std::stoull(sd[2]);
83 pCamera->mActiveDevices.push_back(aDevice);
84 }
85 }
86 }
87
88 //Return 0 if NO, otherwise Cam IDX
IsDevSceneInCamera(const unsigned char DevSceneType,const std::string & DevSceneID)89 uint64_t CCameraHandler::IsDevSceneInCamera(const unsigned char DevSceneType, const std::string &DevSceneID)
90 {
91 return IsDevSceneInCamera(DevSceneType, std::stoull(DevSceneID));
92 }
93
IsDevSceneInCamera(const unsigned char DevSceneType,const uint64_t DevSceneID)94 uint64_t CCameraHandler::IsDevSceneInCamera(const unsigned char DevSceneType, const uint64_t DevSceneID)
95 {
96 std::lock_guard<std::mutex> l(m_mutex);
97 for (const auto & itt : m_cameradevices)
98 {
99 for (const auto & itt2 : itt.mActiveDevices)
100 {
101 if (
102 (itt2.DevSceneType == DevSceneType) &&
103 (itt2.DevSceneRowID == DevSceneID)
104 )
105 return itt.ID;
106 }
107 }
108 return 0;
109 }
110
GetCameraURL(const std::string & CamID)111 std::string CCameraHandler::GetCameraURL(const std::string &CamID)
112 {
113 cameraDevice* pCamera = GetCamera(CamID);
114 if (pCamera == NULL)
115 return "";
116 return GetCameraURL(pCamera);
117 }
118
GetCameraURL(const uint64_t CamID)119 std::string CCameraHandler::GetCameraURL(const uint64_t CamID)
120 {
121 cameraDevice* pCamera = GetCamera(CamID);
122 if (pCamera == NULL)
123 return "";
124 return GetCameraURL(pCamera);
125 }
126
GetCameraURL(cameraDevice * pCamera)127 std::string CCameraHandler::GetCameraURL(cameraDevice *pCamera)
128 {
129 std::stringstream s_str;
130
131 bool bHaveUPinURL = (pCamera->ImageURL.find("#USERNAME") != std::string::npos) || (pCamera->ImageURL.find("#PASSWORD") != std::string::npos);
132
133 std::string szURLPreFix = (pCamera->Protocol == CPROTOCOL_HTTP) ? "http" : "https";
134
135 if ((!bHaveUPinURL) && ((pCamera->Username != "") || (pCamera->Password != "")))
136 s_str << szURLPreFix << "://" << CURLEncode::URLEncode(pCamera->Username) << ":" << CURLEncode::URLEncode(pCamera->Password) << "@" << pCamera->Address << ":" << pCamera->Port;
137 else
138 s_str << szURLPreFix << "://" << pCamera->Address << ":" << pCamera->Port;
139 return s_str.str();
140 }
141
GetCamera(const std::string & CamID)142 CCameraHandler::cameraDevice* CCameraHandler::GetCamera(const std::string &CamID)
143 {
144 return GetCamera(std::stoull(CamID));
145 }
146
GetCamera(const uint64_t CamID)147 CCameraHandler::cameraDevice* CCameraHandler::GetCamera(const uint64_t CamID)
148 {
149 for (auto & itt : m_cameradevices)
150 {
151 if (itt.ID == CamID)
152 return &itt;
153 }
154 return NULL;
155 }
156
TakeSnapshot(const std::string & CamID,std::vector<unsigned char> & camimage)157 bool CCameraHandler::TakeSnapshot(const std::string &CamID, std::vector<unsigned char> &camimage)
158 {
159 return TakeSnapshot(std::stoull(CamID), camimage);
160 }
161
TakeRaspberrySnapshot(std::vector<unsigned char> & camimage)162 bool CCameraHandler::TakeRaspberrySnapshot(std::vector<unsigned char> &camimage)
163 {
164 std::string raspparams = "-w 800 -h 600 -t 1";
165 m_sql.GetPreferencesVar("RaspCamParams", raspparams);
166
167 std::string OutputFileName = szUserDataFolder + "tempcam.jpg";
168
169 std::string raspistillcmd = "raspistill " + raspparams + " -o " + OutputFileName;
170 std::remove(OutputFileName.c_str());
171
172 //Get our image
173 int ret = system(raspistillcmd.c_str());
174 if (ret != 0)
175 {
176 _log.Log(LOG_ERROR, "Error executing raspistill command. returned: %d", ret);
177 return false;
178 }
179 //If all went correct, we should have our file
180 try
181 {
182 std::ifstream is(OutputFileName.c_str(), std::ios::in | std::ios::binary);
183 if (is)
184 {
185 if (is.is_open())
186 {
187 char buf[512];
188 while (is.read(buf, sizeof(buf)).gcount() > 0)
189 camimage.insert(camimage.end(), buf, buf + (unsigned int)is.gcount());
190 is.close();
191 std::remove(OutputFileName.c_str());
192 return true;
193 }
194 }
195 }
196 catch (...)
197 {
198
199 }
200
201 return false;
202 }
203
TakeUVCSnapshot(const std::string & device,std::vector<unsigned char> & camimage)204 bool CCameraHandler::TakeUVCSnapshot(const std::string &device, std::vector<unsigned char> &camimage)
205 {
206 std::string uvcparams = "-S80 -B128 -C128 -G80 -x800 -y600 -q100";
207 m_sql.GetPreferencesVar("UVCParams", uvcparams);
208
209 std::string OutputFileName = szUserDataFolder + "tempcam.jpg";
210 std::string nvcmd = "uvccapture " + uvcparams + " -o" + OutputFileName;
211 if (!device.empty()) {
212 nvcmd += " -d/dev/" + device;
213 }
214 std::remove(OutputFileName.c_str());
215
216 try
217 {
218 //Get our image
219 int ret = system(nvcmd.c_str());
220 if (ret != 0)
221 {
222 _log.Log(LOG_ERROR, "Error executing uvccapture command. returned: %d", ret);
223 return false;
224 }
225 //If all went correct, we should have our file
226 std::ifstream is(OutputFileName.c_str(), std::ios::in | std::ios::binary);
227 if (is)
228 {
229 if (is.is_open())
230 {
231 char buf[512];
232 while (is.read(buf, sizeof(buf)).gcount() > 0)
233 camimage.insert(camimage.end(), buf, buf + (unsigned int)is.gcount());
234 is.close();
235 std::remove(OutputFileName.c_str());
236 return true;
237 }
238 }
239 }
240 catch (...)
241 {
242
243 }
244 return false;
245 }
246
TakeSnapshot(const uint64_t CamID,std::vector<unsigned char> & camimage)247 bool CCameraHandler::TakeSnapshot(const uint64_t CamID, std::vector<unsigned char> &camimage)
248 {
249 std::lock_guard<std::mutex> l(m_mutex);
250
251 cameraDevice *pCamera = GetCamera(CamID);
252 if (pCamera == NULL)
253 return false;
254
255 std::string szURL = GetCameraURL(pCamera);
256 szURL += "/" + pCamera->ImageURL;
257 stdreplace(szURL, "#USERNAME", pCamera->Username);
258 stdreplace(szURL, "#PASSWORD", pCamera->Password);
259
260 if (pCamera->ImageURL == "raspberry.cgi")
261 return TakeRaspberrySnapshot(camimage);
262 else if (pCamera->ImageURL == "uvccapture.cgi")
263 return TakeUVCSnapshot(pCamera->Username, camimage);
264
265 std::vector<std::string> ExtraHeaders;
266 return HTTPClient::GETBinary(szURL, ExtraHeaders, camimage, 5);
267 }
268
WrapBase64(const std::string & szSource,const size_t lsize=72)269 std::string WrapBase64(const std::string &szSource, const size_t lsize = 72)
270 {
271 std::string cstring = szSource;
272 std::string ret = "";
273 while (cstring.size() > lsize)
274 {
275 std::string pstring = cstring.substr(0, lsize);
276 if (!ret.empty())
277 ret += '\n';
278 ret += pstring;
279 cstring = cstring.substr(lsize);
280 }
281 if (!cstring.empty())
282 {
283 ret += '\n' + cstring;
284 }
285 return ret;
286 }
287
EmailCameraSnapshot(const std::string & CamIdx,const std::string & subject)288 bool CCameraHandler::EmailCameraSnapshot(const std::string &CamIdx, const std::string &subject)
289 {
290 int nValue;
291 if (!m_sql.GetPreferencesVar("EmailEnabled", nValue))
292 {
293 return false;//no email setup
294 }
295 if (!nValue)
296 return false; //disabled
297
298 std::string sValue;
299 if (!m_sql.GetPreferencesVar("EmailServer", sValue))
300 {
301 return false;//no email setup
302 }
303 if (sValue == "")
304 {
305 return false;//no email setup
306 }
307 if (CamIdx == "")
308 return false;
309
310 std::vector<std::string> splitresults;
311 StringSplit(CamIdx, ";", splitresults);
312
313 std::string EmailFrom;
314 std::string EmailTo;
315 std::string EmailServer = sValue;
316 int EmailPort = 25;
317 std::string EmailUsername;
318 std::string EmailPassword;
319 int EmailAsAttachment = 0;
320 m_sql.GetPreferencesVar("EmailFrom", EmailFrom);
321 m_sql.GetPreferencesVar("EmailTo", EmailTo);
322 m_sql.GetPreferencesVar("EmailUsername", EmailUsername);
323 m_sql.GetPreferencesVar("EmailPassword", EmailPassword);
324 m_sql.GetPreferencesVar("EmailPort", EmailPort);
325 m_sql.GetPreferencesVar("EmailAsAttachment", EmailAsAttachment);
326 std::string htmlMsg =
327 "<html>\r\n"
328 "<body>\r\n";
329
330 SMTPClient sclient;
331 sclient.SetFrom(CURLEncode::URLDecode(EmailFrom.c_str()));
332 sclient.SetTo(CURLEncode::URLDecode(EmailTo.c_str()));
333 sclient.SetCredentials(base64_decode(EmailUsername), base64_decode(EmailPassword));
334 sclient.SetServer(CURLEncode::URLDecode(EmailServer.c_str()), EmailPort);
335 sclient.SetSubject(CURLEncode::URLDecode(subject));
336
337 for (const auto & camIt : splitresults)
338 {
339 std::vector<unsigned char> camimage;
340
341 if (!TakeSnapshot(camIt, camimage))
342 return false;
343
344 std::vector<char> filedata;
345 filedata.insert(filedata.begin(), camimage.begin(), camimage.end());
346 std::string imgstring;
347 imgstring.insert(imgstring.end(), filedata.begin(), filedata.end());
348 imgstring = base64_encode(imgstring);
349 imgstring = WrapBase64(imgstring);
350
351 htmlMsg +=
352 "<img src=\"data:image/jpeg;base64,";
353 htmlMsg +=
354 imgstring +
355 "\">\r\n";
356 if (EmailAsAttachment != 0)
357 sclient.AddAttachment(imgstring, "snapshot" + camIt + ".jpg");
358 }
359 if (EmailAsAttachment == 0)
360 sclient.SetHTMLBody(htmlMsg);
361 bool bRet = sclient.SendEmail();
362 return bRet;
363 }
364
365 //Webserver helpers
366 namespace http {
367 namespace server {
RType_Cameras(WebEmSession & session,const request & req,Json::Value & root)368 void CWebServer::RType_Cameras(WebEmSession & session, const request& req, Json::Value &root)
369 {
370 if (session.rights < 2)
371 {
372 session.reply_status = reply::forbidden;
373 return; //Only admin user allowed
374 }
375
376 std::string rused = request::findValue(&req, "used");
377
378 root["status"] = "OK";
379 root["title"] = "Cameras";
380
381 std::vector<std::vector<std::string> > result;
382 if (rused == "true") {
383 result = m_sql.safe_query("SELECT ID, Name, Enabled, Address, Port, Username, Password, ImageURL, Protocol FROM Cameras WHERE (Enabled=='1') ORDER BY ID ASC");
384 }
385 else {
386 result = m_sql.safe_query("SELECT ID, Name, Enabled, Address, Port, Username, Password, ImageURL, Protocol FROM Cameras ORDER BY ID ASC");
387 }
388 if (!result.empty())
389 {
390 int ii = 0;
391 for (const auto & itt : result)
392 {
393 std::vector<std::string> sd = itt;
394
395 root["result"][ii]["idx"] = sd[0];
396 root["result"][ii]["Name"] = sd[1];
397 root["result"][ii]["Enabled"] = (sd[2] == "1") ? "true" : "false";
398 root["result"][ii]["Address"] = sd[3];
399 root["result"][ii]["Port"] = atoi(sd[4].c_str());
400 root["result"][ii]["Username"] = base64_decode(sd[5]);
401 root["result"][ii]["Password"] = base64_decode(sd[6]);
402 root["result"][ii]["ImageURL"] = sd[7];
403 root["result"][ii]["Protocol"] = atoi(sd[8].c_str());
404 ii++;
405 }
406 }
407 }
RType_CamerasUser(WebEmSession & session,const request & req,Json::Value & root)408 void CWebServer::RType_CamerasUser(WebEmSession& session, const request& req, Json::Value& root)
409 {
410 root["status"] = "OK";
411 root["title"] = "Cameras";
412
413 std::vector<std::vector<std::string> > result;
414 result = m_sql.safe_query("SELECT ID, Name FROM Cameras WHERE (Enabled=='1') ORDER BY ID ASC");
415 if (!result.empty())
416 {
417 int ii = 0;
418 for (const auto& itt : result)
419 {
420 std::vector<std::string> sd = itt;
421
422 root["result"][ii]["idx"] = sd[0];
423 root["result"][ii]["Name"] = sd[1];
424 ii++;
425 }
426 }
427 }
GetInternalCameraSnapshot(WebEmSession & session,const request & req,reply & rep)428 void CWebServer::GetInternalCameraSnapshot(WebEmSession & session, const request& req, reply & rep)
429 {
430 std::string request_path;
431 request_handler::url_decode(req.uri, request_path);
432
433 std::vector<unsigned char> camimage;
434 if (request_path.find("raspberry") != std::string::npos)
435 {
436 if (!m_mainworker.m_cameras.TakeRaspberrySnapshot(camimage)) {
437 return;
438 }
439 }
440 else
441 {
442 if (!m_mainworker.m_cameras.TakeUVCSnapshot("", camimage)) {
443 return;
444 }
445 }
446 reply::set_content(&rep, camimage.begin(), camimage.end());
447 reply::add_header_attachment(&rep, "snapshot.jpg");
448 }
449
GetCameraSnapshot(WebEmSession & session,const request & req,reply & rep)450 void CWebServer::GetCameraSnapshot(WebEmSession & session, const request& req, reply & rep)
451 {
452 std::vector<unsigned char> camimage;
453 std::string idx = request::findValue(&req, "idx");
454 if (idx == "") {
455 return;
456 }
457 if (!m_mainworker.m_cameras.TakeSnapshot(idx, camimage)) {
458 return;
459 }
460 reply::set_content(&rep, camimage.begin(), camimage.end());
461 reply::add_header_attachment(&rep, "snapshot.jpg");
462 }
463
Cmd_AddCamera(WebEmSession & session,const request & req,Json::Value & root)464 void CWebServer::Cmd_AddCamera(WebEmSession & session, const request& req, Json::Value &root)
465 {
466 if (session.rights < 2)
467 {
468 session.reply_status = reply::forbidden;
469 return; //Only admin user allowed
470 }
471
472 std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
473 std::string senabled = request::findValue(&req, "enabled");
474 std::string address = HTMLSanitizer::Sanitize(request::findValue(&req, "address"));
475 std::string sport = request::findValue(&req, "port");
476 std::string username = HTMLSanitizer::Sanitize(request::findValue(&req, "username"));
477 std::string password = request::findValue(&req, "password");
478 std::string timageurl = HTMLSanitizer::Sanitize(request::findValue(&req, "imageurl"));
479 int cprotocol = atoi(request::findValue(&req, "protocol").c_str());
480 if (
481 (name == "") ||
482 (address == "") ||
483 (timageurl == "")
484 )
485 return;
486
487 std::string imageurl;
488 if (request_handler::url_decode(timageurl, imageurl))
489 {
490 imageurl = base64_decode(imageurl);
491
492 int port = atoi(sport.c_str());
493 root["status"] = "OK";
494 root["title"] = "AddCamera";
495 m_sql.safe_query(
496 "INSERT INTO Cameras (Name, Enabled, Address, Port, Username, Password, ImageURL, Protocol) VALUES ('%q',%d,'%q',%d,'%q','%q','%q',%d)",
497 name.c_str(),
498 (senabled == "true") ? 1 : 0,
499 address.c_str(),
500 port,
501 base64_encode(username).c_str(),
502 base64_encode(password).c_str(),
503 imageurl.c_str(),
504 cprotocol
505 );
506 m_mainworker.m_cameras.ReloadCameras();
507 }
508 }
509
Cmd_UpdateCamera(WebEmSession & session,const request & req,Json::Value & root)510 void CWebServer::Cmd_UpdateCamera(WebEmSession & session, const request& req, Json::Value &root)
511 {
512 if (session.rights < 2)
513 {
514 session.reply_status = reply::forbidden;
515 return; //Only admin user allowed
516 }
517
518 std::string idx = request::findValue(&req, "idx");
519 if (idx == "")
520 return;
521 std::string name = HTMLSanitizer::Sanitize(request::findValue(&req, "name"));
522 std::string senabled = request::findValue(&req, "enabled");
523 std::string address = HTMLSanitizer::Sanitize(request::findValue(&req, "address"));
524 std::string sport = request::findValue(&req, "port");
525 std::string username = HTMLSanitizer::Sanitize(request::findValue(&req, "username"));
526 std::string password = request::findValue(&req, "password");
527 std::string timageurl = HTMLSanitizer::Sanitize(request::findValue(&req, "imageurl"));
528 int cprotocol = atoi(request::findValue(&req, "protocol").c_str());
529 if (
530 (name == "") ||
531 (senabled == "") ||
532 (address == "") ||
533 (timageurl == "")
534 )
535 return;
536
537 std::string imageurl;
538 if (request_handler::url_decode(timageurl, imageurl))
539 {
540 imageurl = base64_decode(imageurl);
541
542 int port = atoi(sport.c_str());
543
544 root["status"] = "OK";
545 root["title"] = "UpdateCamera";
546
547 m_sql.safe_query(
548 "UPDATE Cameras SET Name='%q', Enabled=%d, Address='%q', Port=%d, Username='%q', Password='%q', ImageURL='%q', Protocol=%d WHERE (ID == '%q')",
549 name.c_str(),
550 (senabled == "true") ? 1 : 0,
551 address.c_str(),
552 port,
553 base64_encode(username).c_str(),
554 base64_encode(password).c_str(),
555 imageurl.c_str(),
556 cprotocol,
557 idx.c_str()
558 );
559 m_mainworker.m_cameras.ReloadCameras();
560 }
561 }
562
Cmd_DeleteCamera(WebEmSession & session,const request & req,Json::Value & root)563 void CWebServer::Cmd_DeleteCamera(WebEmSession & session, const request& req, Json::Value &root)
564 {
565 if (session.rights < 2)
566 {
567 session.reply_status = reply::forbidden;
568 return; //Only admin user allowed
569 }
570
571 std::string idx = request::findValue(&req, "idx");
572 if (idx == "")
573 return;
574 root["status"] = "OK";
575 root["title"] = "DeleteCamera";
576
577 m_sql.DeleteCamera(idx);
578 m_mainworker.m_cameras.ReloadCameras();
579 }
580 }
581 }
582